from .geometry import Vector

import pygame
import OpenGL.GL as gl
import math

class Controls:
    """Abstract class for controls
    """
    def __init__(self):
        pass

    def apply(self):
        """Apply the controls modification to the model view matrix
        """
        pass

    def update(self, time = 10):
        """Update according to the user's inputs
        """
        pass

class TrackBallControls(Controls):
    """Trackball controls

    Simple trackball controls"""

    def __init__(self):
        """Creates a TrackBallControls

        The trackball is centered at the origin
        """
        super().__init__()
        self.vertex = Vector()
        self.theta = 0

    def apply(self):
        """Apply the rotation of the current trackball
        """
        gl.glRotatef(self.theta * 180 / math.pi, self.vertex.x, self.vertex.y, self.vertex.z)

    def update(self, time = 10):
        """Checks the keyboard inputs and update the angle
        """
        if not pygame.mouse.get_pressed()[0]:
            return

        coeff = 0.001
        move = pygame.mouse.get_rel()

        dV = Vector(move[1] * time * coeff, move[0] * time * coeff, 0)
        dTheta = dV.norm2()

        if abs(dTheta) < 0.00001:
            return

        dV.normalize()

        cosT2 = math.cos(self.theta / 2)
        sinT2 = math.sin(self.theta / 2)
        cosDT2 = math.cos(dTheta / 2)
        sinDT2 = math.sin(dTheta / 2)

        A = cosT2 * sinDT2 * dV + cosDT2 * sinT2 * self.vertex + sinDT2 * sinT2 * Vector.cross_product(dV, self.vertex)

        self.theta = 2 * math.acos(cosT2 * cosDT2 - sinT2 * sinDT2 * Vector.dot(dV, self.vertex))

        self.vertex = A
        self.vertex.normalize()

class OrbitControls(Controls):
    """Simple OrbitControls

    Similar to TrackBallControls but the up vector is preserved"""

    def __init__(self):
        """Creates an OrbitControls with null angles
        """
        super().__init__()
        self.phi = 0
        self.theta = 0
        self.scale_log = 0

    def apply(self):
        scale = math.exp(self.scale_log)
        gl.glScalef(scale, scale, scale)
        gl.glRotatef(self.theta * 180 / math.pi, 1.0, 0.0, 0.0)
        gl.glRotatef(self.phi * 180 / math.pi, 0.0, 1.0, 0.0)

    def apply_event(self, event):
        """Manages the wheel event

        :param event: a pyevent
        """
        if event.type == pygame.MOUSEBUTTONDOWN:
            # Wheel up
            if event.button == 4:
                self.scale_log += 0.1
            # Wheel down
            elif event.button == 5:
                self.scale_log -= 0.1

    def update(self, time = 10):

        if not pygame.mouse.get_pressed()[0]:
            return

        move = pygame.mouse.get_rel()
        self.theta += move[1] * 0.01
        self.phi += move[0] * 0.01

        self.theta = max(min(self.theta, math.pi / 2), -math.pi / 2)