import pygame
from coopstructs.geometry.vectors.vectorN import Vector2
from coopstructs.toggles import BooleanToggleable
from coopgame.gameTemplate import GameTemplate
from coopgraph.graphs import Graph, Node
from cooptools.sectors.grids import RectGrid
from cooptools.dictPolicies import TogglePolicy
from coopgame.grids_graphs.pygridhandler import PyGridHandler, GridDrawType
from coopgame.grids_graphs.pygraphhandler import PyGraphHandler
from cooptools.toggles import BooleanToggleable
from cooptools.colors import Color
import coopgame.pygamehelpers as help
from coopgame.linedrawing.pyCurveHandler import CurveDrawer
from coopgame.linedrawing.unitpathhandler import UnitPathHandler
from uuid import uuid4
import coopgame.renderedObjectHandling.MatrixManipulation as mm
import numpy as np
import json
import datetime
from coopgame.handleKeyPressedArgs import InputState, InputStateHandler, InputAction, InputEvent, InputEventType, basic_key_actions, CallbackPackage
from coopgame.pygame_k_constant_names import PyKeys, PyMouse

class CurveDrawGame(GameTemplate):

    def __init__(self):
        self.use_scaler_matrix = BooleanToggleable()

        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()),
            InputAction([InputEvent(PyKeys.K_s, event_type=InputEventType.DOWN)], callback=lambda x: self.curves_to_graph()),
            InputAction([InputEvent(PyKeys.K_q, event_type=InputEventType.DOWN)], callback=lambda x: self.use_scaler_matrix.toggle()),
            InputAction([InputEvent(PyKeys.K_d, event_type=InputEventType.DOWN)], callback=lambda x: self.toggle_show_toggled()),
            InputAction([InputEvent(PyKeys.K_p, event_type=InputEventType.DOWN)], callback=lambda x: self.persist_graph_to_json()),
            InputAction([InputEvent(PyKeys.K_g, event_type=InputEventType.DOWN)], callback=lambda x: self.persist_grid()),
            InputAction([InputEvent(PyKeys.K_l, event_type=InputEventType.DOWN)], callback=lambda x: self.load_grid()),
        ])

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

        self.toggle_key = 'toggle'
        self.unit_path_handler = UnitPathHandler(self.toggle_key)
        self.toggle_bool_policy = TogglePolicy(key=self.toggle_key, toggle_type=BooleanToggleable, default=False)
        self.grid = RectGrid(20, 20, default_state={})
        for pos, state in self.grid.grid_enumerator:
            self.toggle_bool_policy.initialize(state.state)

        self.grid_background_surface = None
        self.grid_overlay_surface = None
        self.curves_surface = None
        self.graph_surface = None

        self.grid_handler = PyGridHandler()
        self.graph_handler = PyGraphHandler()
        self.curves = []
        self.graph = None
        self.curve_drawer = CurveDrawer()

        self.x_rot = 0
        self.y_rot = 0
        self.time_since_report_stats = 0

        self.show_toggled = False

        self._update_grid_background_surface()


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

    def _regenerate_curves(self):
        self.curves = self.unit_path_handler.generate(self.grid,
                                        self.grid_handler.grid_box_rectangle(self.screen.get_width(), self.screen.get_height(), self.grid, margin=0),
                                        naming_provider=self.get_next_id)

    def handle_left_mouse_down(self):
        grid_pos = self.mouse_grid_pos()
        self.grid.act_at_loc(grid_pos[0], grid_pos[1], [self.toggle_bool_policy])
        self._regenerate_curves()
        self._update_curves_surface()

    def on_resize(self):
        self._update_grid_background_surface()
        self._regenerate_curves()

    def mouse_grid_pos(self):
        return self.grid_handler.get_mouse_grid_pos(self.game_area_rectangle,
                                                    help.mouse_pos_as_vector(),
                                             draw_scale_matrix=self.draw_scale_matrix(),
                                             coord_to_grid_converter=self.grid.grid_from_coord)

    def toggle_show_toggled(self):
        self.show_toggled = not self.show_toggled

    def _update_grid_background_surface(self):
        self.grid_background_surface = help.init_surface(self.screen.get_size())
        self.grid_handler.draw_base_to_surface(self.grid_background_surface,
                                               self.grid, grid_color=Color.DARK_GREY,
                                               grid_draw_type=GridDrawType.LINES,
                                               draw_scale_matrix=self.draw_scale_matrix())

    def _update_grid_overlay_surface(self):
        self.grid_overlay_surface = help.init_surface(self.screen.get_size())
        self.grid_handler.draw_overlay_to_surface(surface=self.grid_overlay_surface,
                                                  grid=self.grid,
                                                  hover=(Vector2.from_tuple(self.mouse_grid_pos()), Color.PURPLE),
                                                  toggled=(self.toggle_key, Color.ORANGE) if self.show_toggled else None,
                                                  draw_scale_matrix=self.draw_scale_matrix())

    def _update_curves_surface(self):
        self.curves_surface = help.init_surface(self.screen.get_size())
        self.curve_drawer.draw_curves({x: Color.GREEN for x in self.curves}, self.curves_surface)

    def _update_graph_surface(self):
        self.graph_surface = help.init_surface(self.screen.get_size())
        if self.graph:
            self.graph_handler.draw_to_surface(self.graph_surface, self.graph)

    def draw(self, frames:int, debug_mode: bool = False):
        self._update_grid_overlay_surface()

        # draw grid base
        if self.grid_background_surface is not None:
            self.screen.blit(self.grid_background_surface, (0, 0))

        # draw grid overlay
        if self.grid_overlay_surface is not None:
            self.screen.blit(self.grid_overlay_surface, (0, 0))

        # draw unit paths
        if self.curves_surface is not None:
            self.screen.blit(self.curves_surface, (0, 0))

        #draw graph
        if self.graph_surface is not None:
            self.screen.blit(self.graph_surface, (0, 0))


    def curves_to_graph(self):

        a = np.asarray([(curve.origin, curve.end_point, type(curve)) for curve in self.curves])

        point_names = {}
        for o, d, _ in a:
            if not o in point_names:
                point_names[o] = self.get_next_id()

            if not d in point_names:
                point_names[d] = self.get_next_id()


        g = {}
        for o, d, t in a:
            n_o = Node(name=point_names[o], pos=o)
            n_d = Node(name=point_names[d], pos=d)
            g.setdefault(n_o, [])
            g.setdefault(n_d, [])

            g[n_o].append(n_d)
            g[n_d].append(n_o)

        self.graph = Graph(g)

        print(self.graph)

        self._update_graph_surface()

    def persist_graph_to_json(self):
        if self.graph is None:
            return

        my_zone = self.get_next_id()
        my_json_dict = {}

        my_json_dict["version"] = "0.0.1"
        my_json_dict["dateGenerated"] = str(datetime.datetime.utcnow().isoformat())
        my_json_dict["nodes"] = [{"nodeId": node.id,
                                  "label": "some node label",
                                  "type": "node",
                                  "locationId": ["ASDFG"],
                                  "actions": [{
                                               "zHeight":1,
                                               "action":"put",
                                               "locationId":""
                                            },
                                              {
                                                  "zHeight": 1,
                                                  "action": "pick",
                                                  "locationId": ""
                                              }
                                  ] ,
                                 "zones": [my_zone]
                                  } for node in self.graph.nodes()]
        my_json_dict["zones"] = {
            "id": my_zone,
            "zoneActions": [
                "honk"
            ],
            "enclosedNodes": [node.id for node in self.graph.nodes()],
            "polygonPoints": [{
                                       "x":1,
                                       "y":1
                                    },
                                    {
                                       "x":1,
                                       "y":1
                                    },
                                    {
                                       "x":1,
                                       "y":1
                                    },
                                    {
                                       "x":1,
                                       "y":1
                                    }],
            "metaData": {}
        }
        my_json_dict["agents"] = [
          {
             "agentId":"srt9600",
             "version":"0.0.0"
          }
       ]
        my_json_dict["graphs"] = {
            "srt9600": {
                "default":{
                    node.id: {
                        "location":{
                            "x": int(node.focal_point.x),
                            "y": int(node.focal_point.y),
                            "z": 0
                        },
                        "inHeadingDegree": 90,
                        "outHeadingDegree": 90,
                        "edges": {
                            self.graph._edges[edge_id].id:{
                                "destNode": self.graph._edges[edge_id].end.id,
                                "distEstimate": self.graph._edges[edge_id].length,
                                "blockedNodes":[],
                                "curves": [],
                                "metadta": {}
                            }
                            for edge_id in edge_ids
                        }

                    } for node, edge_ids in self.graph._node_edge_map.items()
                }
            }
        }


        my_json = json.dumps(my_json_dict, indent=4)
        print(my_json)

        with open('coop_generated_path.json', 'w') as outfile:
            outfile.write(my_json)

    def persist_grid(self):
        data_to_save = self.grid.state_value_as_array(self.toggle_key)

        my_json = json.dumps(data_to_save, indent=4)

        print(my_json)

    def load_grid(self):
        loaded_data = json.load()

    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, self.y_rot)
        else:
            return None

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

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


