from coopgame.gameTemplate import GameTemplate
from coopstructs.vectors import Vector2
from coopstructs.geometry import Rectangle as cRect
import pygame
from cooptools.colors import Color
from coopgame.spriteHandling.sprites import RectangleSprite, AnimatedSprite
from coopgame.grids_graphs.pygridhandler import PyGridHandler
from coopgame.grids_graphs.pygraphhandler import PyGraphHandler
from coopgraph.grids import RectGrid
from coopgraph.gridgraph import GridGraph
from coopgame.enums import GridDrawType
from coopgame.linedrawing.unitpathhandler import UnitPathHandler
import coopgame.pygamehelpers as help
from coopstructs.geometry import Rectangle
from coopgraph.gridSelectPolicies import TogglePolicy
from coopgraph.toggles import BooleanToggleable
import coopgame.renderedObjectHandling.MatrixManipulation as mm
import numpy as np
from pygame import mixer
from coopgame.spriteHandling.spritesheet import SpriteSheet
from coopgame.linedrawing.pyCurveHandler import CurveDrawer
from coopstructs.curves import LineCurve

class MyGame(GameTemplate):
    def __init__(self):
        super().__init__(fullscreen=False)

        self.agents = {}
        self.sprite_group = pygame.sprite.Group()

        self.register_action_to_keys((pygame.K_r,), self.sprite_group.empty)
        self.register_action_to_keys((pygame.K_SPACE,), self.create_yellow)
        self.register_action_to_keys((pygame.K_o,), self.set_astar_origin)
        self.register_action_to_keys((pygame.K_t,), self.toggle_show_graph)
        self.register_action_to_keys((pygame.K_g,), self.toggle_show_grid)
        self.register_action_to_keys((pygame.K_h,), self.toggle_show_grid_highlights)
        self.register_action_to_keys((pygame.K_m,), self.increase_x_rotation)
        self.register_action_to_keys((pygame.K_n,), self.decrease_x_rotation)
        self.register_action_to_keys((pygame.K_w,), lambda: self.set_character_animation("walkback"))
        self.register_action_to_keys((pygame.K_a,), lambda: self.set_character_animation("walkleft"))
        self.register_action_to_keys((pygame.K_s,), lambda: self.set_character_animation("walkdown"))
        self.register_action_to_keys((pygame.K_d,), lambda: self.set_character_animation("walkright"))
        self.register_action_to_keys((pygame.K_k,), lambda: self.set_character_animation("fall"))

        self.toggle_name = 'toggle'
        self.toggle_bool_policy = TogglePolicy(key=self.toggle_name, toggle=BooleanToggleable(default=False))
        self.grid = RectGrid(20, 20, grid_select_policies=[self.toggle_bool_policy])
        self.grid_graph = GridGraph(self.grid)
        self.grid_drawer = PyGridHandler()
        self.graph_drawer = PyGraphHandler()
        self.unit_path_drawer = UnitPathHandler(self.toggle_name)

        self.init_dynamic_surfaces()

        self.grid_foreground_surface = None
        self.grid_background_surface = None
        self.graph_surface = None

        self.curve_drawer = CurveDrawer()

        self.init_static_surfaces()
        self.init_dynamic_surfaces()

        self.x_rot = 1.1
        self._update_background_surfaces(Vector2(0, 0), Vector2(0, 0))
        self._update_foreground_surfaces(Vector2(0, 0), Vector2(0, 0))

        self._astar_curves = None
        self._astar_start_point = Vector2(0, 0)

        self.show_graph = False
        self.show_grid = True
        self.show_grid_highlights = True

        self.time_since_report_stats = 0

        self._index = 0

        # character_file = 'coopgame/assets/mage walking poses sheet copy.png'
        # self.character_spritesheet = SpriteSheet(character_file, n_rows=4, n_columns=9, x_margin=17, x_padding=34, y_margin=9, y_padding=10)
        character_file = '../coopgame/assets/spriteSheets/DA_male2.png'
        self.character_spritesheet = SpriteSheet(character_file, n_rows=21, n_columns=13, x_margin_left=17, x_padding=34, y_margin_top=15, y_padding=17, y_margin_bottom=1)

        char_pos = self.grid_drawer.grid_point_to_viewport_point(self.screen, self.grid, Vector2(0, 0), draw_scale_matrix=self.get_grid_rotation_matrix())
        char_pos = Vector2(char_pos.x, char_pos.y -256//4//2)
        self.main_character = AnimatedSprite("main"
                                             , char_pos
                                             , {"walkback": tuple(self.character_spritesheet.load_row_strip(8, colorkey=-1)[:9]),
                                                "walkleft": tuple(self.character_spritesheet.load_row_strip(9, colorkey=-1)[:9]),
                                                "walkdown": tuple(self.character_spritesheet.load_row_strip(10, colorkey=-1)[1:9]),
                                                "walkright": tuple(self.character_spritesheet.load_row_strip(11, colorkey=-1)[:9]),
                                                "fall": tuple(self.character_spritesheet.load_row_strip(20, colorkey=-1)[:6])}
                                             , default_animation_key="walkdown")

        self.animate_timer = 0
        self.animate_cycle = 100

    def initialize_game(self):
        mixer.music.load('../coopgame/assets/music/cute_bass.mp3')
        mixer.music.play(loops=-1)


    def set_character_animation(self, animation_key):
        self.main_character.set_animation(animation_key)

    def increase_x_rotation(self):
        self.x_rot += .1
        self._update_background_surfaces(Vector2(0, 0), Vector2(0, 0))
        self._update_foreground_surfaces(Vector2(0, 0), Vector2(0, 0))

    def decrease_x_rotation(self):
        self.x_rot -= .1
        self._update_background_surfaces(Vector2(0, 0), Vector2(0, 0))
        self._update_foreground_surfaces(Vector2(0, 0), Vector2(0, 0))

    def get_grid_rotation_matrix(self):
        orientation = np.array([0, 0, 1])
        rotation_unit_vector = mm.rotation_unit_vector(orientation)
        matrixz = mm.rotateAroundAxis((self.screen_width/2, self.screen_height / 2, 0), rotation_unit_vector, np.pi/4)
        orientation = np.array([1, 0, 0])
        rotation_unit_vector = mm.rotation_unit_vector(orientation)
        matrixx = mm.rotateAroundAxis((self.screen_width / 2, self.screen_height / 2, 0), rotation_unit_vector,
                                      self.x_rot)
        # matrixz = mm.rotateZMatrix(np.pi/2)
        # # matrixx = mm.rotateXMatrix(np.pi/4)
        matrix = matrixx.dot(matrixz)
        # return matrix
        return None

    def get_next_index(self):
        self._index += 1
        return self._index

    def init_dynamic_surfaces(self):
        self.grid_foreground_surface = pygame.Surface(self.screen.get_size()).convert()
        self.grid_foreground_surface.set_colorkey(Color.BLACK.value)

    def init_static_surfaces(self):
        self.grid_background_surface = pygame.Surface(self.screen.get_size()).convert()
        self.graph_surface = pygame.Surface(self.screen.get_size()).convert()

        self.grid_background_surface.set_colorkey(Color.BLACK.value)
        self.graph_surface.set_colorkey(Color.BLACK.value)

    def _update_background_surfaces(self, old_pos: Vector2, new_pos: Vector2):
        self.init_static_surfaces()
        self.graph_surface.fill(color=Color.BLACK.value)

        self.graph_drawer.draw_to_surface(self.graph_surface, self.grid_graph.graph, coordinate_converter=self.graph_to_coord, node_color=Color.ORANGE, enabled_edge_color=Color.BRIGHT_BLUE, disabled_edge_color=Color.GREY, draw_scale_matrix=self.get_grid_rotation_matrix())
        self.grid_drawer.draw_base_to_surface(self.grid_background_surface, self.grid, grid_color=Color.DARK_GREY, grid_draw_type=GridDrawType.LINES, draw_scale_matrix=self.get_grid_rotation_matrix())

    def _update_foreground_surfaces(self, old_pos: Vector2, new_pos: Vector2):
        self.init_dynamic_surfaces()
        self.grid_foreground_surface.fill(color=Color.BLACK.value)
        highlights = self.define_highlighted_grids(hover=new_pos)
        self.grid_drawer.draw_overlay_to_surface(self.grid_foreground_surface, self.grid, additional_highlight_grid_cells=highlights, draw_scale_matrix=self.get_grid_rotation_matrix())

        # self.unit_path_drawer.draw(self.grid, self.grid_drawer, self.grid_foreground_surface)

    def draw(self, frames:int, debug_mode: bool = False):
        #draw grid background
        if self.show_grid:
            self.screen.blit(self.grid_background_surface, (0, 0))

        #draw grid foreground
        if self.show_grid_highlights:
            self.screen.blit(self.grid_foreground_surface, (0, 0))

        #draw graph
        if self.show_graph:
            self.screen.blit(self.graph_surface, (0, 0))

        for entity in self.sprite_group:
            entity.blit(self.screen,
                        display_handle=lambda: self.debug_mode_toggle.value,
                        display_rect=lambda: self.debug_mode_toggle.value
                        )
            # self.screen.blit(entity.surf, entity.rect)
            


        if debug_mode:
            self.draw_mouse_coord(self.screen)
            self.draw_fps(self.screen, offset_rect=cRect(0, self.screen.get_height() - 100, 20, 100))
            self.draw_text(f"Sprite Count: {len(self.sprite_group)}", self.screen, offset_rect=Rectangle(0, 90, 100, 200))

        # Draw ASTAR Lines
        [self.curve_drawer.draw_curves({line: Color.YELLOW}, self.screen, draw_scale_matrix=self.get_grid_rotation_matrix()) for line in self._astar_curves or []]

        '''Draw Main Character'''
        self.main_character.blit(self.screen,
                                 display_handle=lambda: self.debug_mode_toggle.value,
                                 display_rect=lambda: self.debug_mode_toggle.value)


    def handle_hover_over(self, pressed_keys, mouse_pos_as_vector: Vector2):
        game_area_rect = cRect(0, 0, self.screen.get_height(), self.screen.get_width())
        self.grid_drawer.handle_hover_over(grid=self.grid, area_rect=game_area_rect,
                                           on_hover_changed_handlers=[self._update_foreground_surfaces, self.calculate_astar_from_cached_pos],
                                           draw_scale_matrix=self.get_grid_rotation_matrix())

    def handle_left_mouse_down(self, pressed_keys, mouse_pos):
        if pressed_keys[pygame.K_LCTRL]:
            grid_pos = self.grid_drawer.get_mouse_grid_pos(self.game_area_rectangle, help.mouse_pos_as_vector(), draw_scale_matrix=self.get_grid_rotation_matrix(), coord_to_grid_converter=self.grid.grid_from_coord)
            self.toggle_at_rc(grid_pos=grid_pos, disabler="BL")
            self._update_background_surfaces(Vector2(0, 0), Vector2(0, 0))
        else:
            name = f"b_{self.get_next_index()}"
            pos = help.mouse_pos_as_vector()
            self.agents[name] = pos
            self.sprite_group.add(RectangleSprite(name, pos, Color.BLUE, 30, 20))

    def handle_right_mouse_down(self, pressed_keys, mouse_pos):
        name = f"r_{self.get_next_index()}"
        pos = help.mouse_pos_as_vector()
        self.agents[name] = pos
        self.sprite_group.add(RectangleSprite(name, pos, Color.RED, 20, 30))

    def create_yellow(self):
        name = f"y_{self.get_next_index()}"
        pos = help.mouse_pos_as_vector()
        self.agents[name] = pos
        self.sprite_group.add(RectangleSprite(name, pos, Color.YELLOW, 20, 30))

    def model_updater(self, delta_time_ms: int):
        self.time_since_report_stats += delta_time_ms
        if self.time_since_report_stats > 10000:
            logging.info(self.tracked_time)
            self.time_since_report_stats = 0

        to_delete = []
        for agent, pos in self.agents.items():
            if agent[0] == "b":
                self.agents[agent] = pos + Vector2(0, 5)
            elif agent[0] == "r":
                self.agents[agent] = pos + Vector2(5, 0)
            elif agent[0] == "y":
                self.agents[agent] = pos + Vector2(5, 5)

            if pos.x > self.screen_width or pos.y > self.screen_height:
                to_delete.append(agent)

        for agent in to_delete:
            del self.agents[agent]


    def sprite_updater(self, delta_time_ms: int):
        char_pos = self.grid_drawer.grid_point_to_viewport_point(self.screen, self.grid, Vector2(0, 0), draw_scale_matrix=self.get_grid_rotation_matrix())
        char_pos = Vector2(char_pos.x, char_pos.y -256//4//2)
        self.main_character.set_pos(char_pos)

        '''animate'''
        self.animate_timer += delta_time_ms
        if self.animate_timer > self.animate_cycle:
            self.animate()

        for sprite in self.sprite_group:
            agent_pos = self.agents.get(sprite.id, None)
            if agent_pos is None:
                self.sprite_group.remove(sprite)
            else:
                sprite.set_pos(agent_pos)


    def animate(self):
        loop = True
        if self.main_character.current_animation == "fall":
            loop = False

        self.main_character.increment_animation_phase(loop)
        self.animate_timer = 0



    def calculate_astar_from_cached_pos(self, old_pos: Vector2, new_pos: Vector2):
        self._astar_curves = self.astar_curves(self.game_area_rectangle, gridGraph=self.grid_graph, start_pos=self._astar_start_point, end_pos=new_pos)

    def astar_curves(self, surface_rect: Rectangle, gridGraph: GridGraph, start_pos: Vector2, end_pos: Vector2):
        astr = [x.focal_point for x in gridGraph.astar_between_grid_pos(start_pos, end_pos).path]

        if astr is not None:
            astr_curves = []
            last = None
            for ii in range(0, len(astr)):
                if last is not None:
                    astr_curves.append(LineCurve(str(self.get_next_index()), gridGraph.grid.coord_from_grid_pos(last, surface_rect), gridGraph.grid.coord_from_grid_pos(astr[ii], surface_rect)))
                last = astr[ii]

            return astr_curves
        else:
            return None

    def set_astar_origin(self):
        self.set_astar_start_point(self.grid_drawer.hover_grid_pos)
        self._astar_curves = None

    def set_astar_start_point(self, grid_coords: Vector2):
        self._astar_start_point = grid_coords

    def define_highlighted_grids(self, hover:Vector2):
        highlights = {}
        grid_point = hover

        if grid_point.x < self.grid.nColumns and grid_point.x >= 0\
                and grid_point.y < self.grid.nRows and grid_point.y >= 0:
            highlights[grid_point] = Color.PURPLE

        # for col in range(0, self.grid.nColumns):
        #     for row in range(0,  self.grid.nRows):
        #         if self.grid.grid[row][col].state[self.toggle_name].value:
        #             highlights[Vector2(col, row)] = Color.GREEN

        return highlights

    def graph_to_coord(self, pos: Vector2):
        rectangle = self.game_area_rectangle
        return self.grid.coord_from_grid_pos(grid_pos=pos, area_rect=rectangle)

    def toggle_show_graph(self):
        self.show_graph = not self.show_graph

    def toggle_show_grid(self):
        self.show_grid = not self.show_grid

    def toggle_show_grid_highlights(self):
        self.show_grid_highlights = not self.show_grid_highlights

    def on_resize(self):
        self._update_background_surfaces(Vector2(0, 0), Vector2(0, 0))
        self._update_foreground_surfaces(Vector2(0, 0), Vector2(0, 0))

    def toggle_at_rc(self, grid_pos, disabler):
        if grid_pos.x < self.grid.nColumns and grid_pos.y < self.grid.nRows:
            self.grid.act_on_grid(grid_pos.y, grid_pos.x, [self.toggle_bool_policy])
            if self.grid.grid[grid_pos.x][grid_pos.y].state[self.toggle_name]:
                self.grid_graph.graph.enable_edges_to_node(Vector2(grid_pos.x, grid_pos.y), disabler)
            else:
                self.grid_graph.graph.disable_edges_to_node(Vector2(grid_pos.x, grid_pos.y), disabler)


            # self.grid.grid[grid_pos.y][grid_pos.x] = not self.grid.grid[grid_pos.y][grid_pos.x]
            # if self.grid.grid[grid_pos.y][grid_pos.x]:
            #     self.grid.enable_edges_to_node(Vector2(grid_pos.x, grid_pos.y), disabler)
            # else:
            #     self.grid.disable_edges_to_node(Vector2(grid_pos.x, grid_pos.y), disabler)



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

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


