from typing import TypeVar
from .__prelude__ import SelectedBand, Matrix, Point, Qt, dynamic
from .GLDrawing import GLDrawing, InitializedDrawing

GenericScene = TypeVar("GenericScene", bound="Scene", covariant=True)

class SceneDrawing[GenericScene](InitializedDrawing[GenericScene]):
    class IdleMode:
        def __init__(self, initialized_scene):
            self.__initialized_scene = initialized_scene

        @property
        def drawing(self):
            return self.__initialized_scene.drawing
        @property
        def initialized_drawing(self):
            return self.__initialized_scene

        def default_transition(self):
            return self

        def on_mouse_enter(self, clip_position: tuple[float, float]):
            return self.default_transition()
        def on_mouse_move(self, clip_position: tuple[float,float]):
            return self.default_transition()
        def on_mouse_leave(self):
            return self.default_transition()

        def on_mouse_button_pressed(self, position: tuple[float, float], button: Qt.Qt.MouseButton):
            return self.default_transition()
        def on_mouse_button_released(self, position: tuple[float, float], button: Qt.Qt.MouseButton):
            return self.default_transition()

        def on_mouse_wheel(self, clip_position: tuple[float, float], angle_x: float, angle_y: float):
            return self.default_transition()

        def on_key_pressed(self, key: int):
            return self.default_transition()
        def on_key_released(self, key: int):
            return self.default_transition()

    def __init__(self, context: Qt.QOpenGLContext, scene: GenericScene):
        super().__init__(context, scene)

    def idle_mode(self):
        return self.IdleMode(self)

    @property
    def world_to_clip(self):
        return Matrix.identity(4)

    def clip_to_model(self, clip_x, clip_y):
        clip_to_model = Matrix.product(self.world_to_clip, self.drawing.model_to_world).inverse()

        v1_model = clip_to_model.transform_point((clip_x, clip_y, -0.5))
        v2_model = clip_to_model.transform_point((clip_x, clip_y, 0.5))
        ray_direction = v2_model - v1_model

        zMul = -v2_model[2]/ray_direction[2]
        return (v2_model[0]+ray_direction[0]*zMul, v2_model[1]+ray_direction[1]*zMul)

class Scene(GLDrawing):
    model_to_world  = dynamic.variable(Matrix.identity(4))

    def initializeDrawing(self, context, /):
        return SceneDrawing(context, self)
