from coopstructs.geometry.vectors.vectorN import Vector2
from coopstructs.geometry.curves.curveBuilder import CurveBuilder
from coopstructs.geometry.curves.curve_factory import curves_from_json
from cooptools.toggles import BooleanToggleable
from coopstructs.geometry import Rectangle
from coopgame.gameTemplate import GameTemplate
import pygame
from cooptools.colors import Color
from uuid import uuid4
import coopgame.renderedObjectHandling.MatrixManipulation as mm
import numpy as np
from coopgame.handleKeyPressedArgs import InputState, InputStateHandler, InputAction, InputEvent, InputEventType, basic_key_actions, CallbackPackage
from coopgame.pygame_k_constant_names import PyKeys, PyMouse
import cooptools.os_manip as osm
from coopui.cli.CliAtomicUserInteraction import CliAtomicUserInteraction as cli
from coopgame.linedrawing.pyCurveHandler import PyCurveHandler

class CurveDrawGame(GameTemplate):

    def __init__(self):
        self.curve_builder = CurveBuilder(id_provider=self.get_next_id)
        input_state_handler = InputStateHandler(
            quit_callback_package=CallbackPackage(down=lambda x: self.quit()),
            debug_callback_package=CallbackPackage(down=lambda x: self.debug_mode_toggle.visibility_toggle()),
            fullscreen_callback_package=CallbackPackage(down=lambda x: self.toggle_fullscreen()),
            key_actions=[
                InputAction([InputEvent(PyMouse.LEFT, event_type=InputEventType.DOWN)], callback=lambda x: self.handle_left_mouse_down(x)),
                InputAction([InputEvent(PyMouse.RIGHT, event_type=InputEventType.DOWN)], callback=lambda x: self.handle_right_mouse_down(x)),
                InputAction([InputEvent(PyKeys.K_s, event_type=InputEventType.DOWN)], callback=lambda x: self.save_curve(x)),
                InputAction([InputEvent(PyKeys.K_SPACE, event_type=InputEventType.DOWN)], callback=lambda x: self.toggle_curve_type(x)),
                InputAction([InputEvent(PyKeys.K_q, event_type=InputEventType.DOWN)], callback=lambda x: self.use_scaler_matrix.toggle()),
                InputAction([InputEvent(PyKeys.K_t, event_type=InputEventType.DOWN)], callback=lambda x: self.curve_builder.toggle_curve_type()),
                InputAction([InputEvent(PyKeys.K_l, event_type=InputEventType.DOWN)], callback=lambda x: self.load_curves()),
        ])


        super().__init__(fullscreen=False, input_state_handler=input_state_handler)



        self.curve_drawer = PyCurveHandler()

        self.use_scaler_matrix = BooleanToggleable(default=False)

        self.x_rot = 1.1
        self.time_since_report_stats = 0

        self.force_continuity = True

    def toggle_curve_type(self, pressed_keys):
        self.curve_builder.toggle_curve_type()

    def save_curve(self, pressed_keys):
        self.curve_builder.save_curve()

    def get_next_id(self):
        return str(uuid4())

    def initialize_game(self):
        pass

    def handle_hover_over(self, mouse_pos_as_vector: Vector2):
        pass

    def handle_left_mouse_down(self, pressed_keys):
        self.curve_builder.add_point(self.next_point())

    def handle_right_mouse_down(self, pressed_keys):
        self.curve_builder.remove_point()

    def increase_x_rotation(self):
        self.x_rot += .1

    def decrease_x_rotation(self):
        self.x_rot -= .1

    def draw(self, frames:int, debug_mode: bool = False):
        # Get saved curves
        curves = {curve: Color.GREEN for id, curve in self.curve_builder.curves.items()}
        next_point = self.next_point()

        try:
            temp_curves = self.curve_builder.curves_from_temp_next_points([next_point])

            # Append proposed curves
            for curve in temp_curves:
                curves[curve] = Color.LIGHT_BLUE
        except:
            pass

        # Draw control points
        for point in self.curve_builder.current_points:
            pygame.draw.circle(self.screen, Color.AQUA.value,
                               (int(point.x), int(point.y)), 4, 0)

        # Draw curves
        self.curve_drawer.draw_curves(curves, self.screen, draw_scale_matrix=self.draw_scale_matrix())

        # Draw next point
        game_area_rect = Rectangle.from_tuple((0, 0, self.screen.get_height(), self.screen.get_width()))
        next_point_on_plane = help.normal_points_to_scaled_points([next_point], draw_scale_matrix=self.draw_scale_matrix())[0]

        pygame.draw.circle(self.screen, Color.GREY.value,
                           (int(next_point_on_plane[0]), int(next_point_on_plane[1])), 4, 1)


    def model_updater(self, delta_time_ms: int):
        pass

    def sprite_updater(self, delta_time_ms: int):
        pass

    def on_resize(self):
        pass

    def next_point(self):
        game_area_rect = Rectangle.from_tuple((0, 0, self.screen.get_height(), self.screen.get_width()))
        if self.force_continuity:
            return self.curve_builder.force_continuity(help.normal_mouse_position(game_area_rect = game_area_rect, draw_scale_matrix=self.draw_scale_matrix()))
        else:
            return help.normal_mouse_position(draw_scale_matrix=self.draw_scale_matrix(), game_area_rect=game_area_rect)

    def draw_scale_matrix(self):
        if self.use_scaler_matrix.value:
            return mm.get_grid_rotation_matrix(self.screen_width/2, self.screen_height / 2,  self.x_rot, np.pi/4)
        else:
            return None

    def load_curves(self):
        fp = r'C:\Users\tburns\PycharmProjects\coopstructs\tests\testData\curves.json'

        if fp is None:
            fp=cli.request_open_filepath(prompt=f"Select a curves file", title="Curves", filetypes=((osm.FileType.JSON.value, ".json"), ))

        if fp is None:
            return

        data = osm.try_load_json_data_to_dict(file_path=fp)
        if data is None:
            return

        self.curve_builder.curves.clear()
        self.curve_builder.add_curves(curves_from_json(data))
        self.curve_builder.save_curve()


if __name__ == "__main__":
    import logging
    import loggingConfig

    loggingConfig.initLogging(loggingLvl=logging.INFO)
    game = CurveDrawGame()
    game.main()


