from coopgame.gameTemplate import GameTemplate
import coopgame.pygamehelpers as help
from cooptools.colors import Color
from coopgame.grids_graphs.pygraphhandler import PyGraphHandler
from cooptools.toggles import BooleanToggleable
from coopgame.handleKeyPressedArgs import InputState, InputStateHandler, InputAction, InputEvent, InputEventType, basic_key_actions, CallbackPackage
from coopgame.pygame_k_constant_names import PyKeys, PyMouse
from coopgraph.graphs import Graph, Node,AStarResults, Edge
from typing import Tuple, List
import string
import random as rnd
import math

from coopgame.grids_graphs.pygraphhandler import GraphSurfaceType
import coopgame.grids_graphs.draw_graph_utils as g_util
import coopgame.linedrawing.line_draw_utils as line_u
import coopgame.pointdrawing.point_draw_utils as point_u
import coopgame.label_drawing.label_drawing_utils as lab_u


NODE_COLOR = Color.AQUA
ENABLED_EDGE_COLOR = Color.ORANGE
DISABLED_EDGE_COLOR = Color.MAROON
NODE_LABEL_COLOR = Color.WHEAT
EDGE_LABEL_COLOR = Color.WHEAT
ARTICULATION_POINTS_COLOR = Color.PURPLE
DIRECTIONALITY_INDICATOR_COLOR = Color.GREEN
NODE_RADIUS = 5

def large_circuit(x_bounds: Tuple[float, float],
                  y_bounds: Tuple[float, float],
                  spread: int = 100,
                  n_nodes: int = 20):
    alphabet=string.ascii_lowercase

    nodes = []
    rnd.seed(0)
    for ii in range(0, n_nodes):
        nodes.append(
            Node(name=alphabet[ii],
                 pos=((x_bounds[1] - x_bounds[0]) / 2 + x_bounds[0] + (x_bounds[1] - x_bounds[0]) * math.sin(ii / n_nodes * 2 * math.pi) + rnd.randint(-spread, spread),
                     (y_bounds[1] - y_bounds[0]) / 2 + y_bounds[0] + (y_bounds[1] - y_bounds[0])  * math.cos(ii / n_nodes * 2 * math.pi) + rnd.randint(-spread, spread))))

    graph_dict = {}

    for ii, node in enumerate(nodes):
        n_connections = rnd.randint(1, 4)

        for n in range(1, n_connections + 1):
            if ii + n >= n_nodes:
                index = ii + n - n_nodes
            else:
                index = ii + n
            graph_dict.setdefault(node, []).append(nodes[index])

    # add an orphan
    graph_dict[Node(name='Orphan_0', pos=(x_bounds[0] + (x_bounds[1] - x_bounds[0]) / 2, y_bounds[0] + (y_bounds[1] - y_bounds[0]) / 2))] = []

    # add a source
    graph_dict[Node(name='Source_0', pos=(x_bounds[0] + (x_bounds[1] - x_bounds[0]) / 2 - 50, y_bounds[0] + (y_bounds[1] - y_bounds[0]) / 2))] = [next(k for k, v in graph_dict.items())]

    # add a sink
    sink = Node(name='Sink_0', pos=(x_bounds[0] + (x_bounds[1] - x_bounds[0]) / 2 + 50, y_bounds[0] + (y_bounds[1] - y_bounds[0]) / 2))
    graph_dict[next(k for k, v in graph_dict.items())].append(sink)
    graph_dict[sink] = []

    return graph_dict


class GraphGame_Basic(GameTemplate):

    def __init__(self):


        self.graph_surface = None
        self.graph_overlay = None

        # toggles
        self.show_graph_base_toggle = BooleanToggleable(default=True)
        self.show_graph_overlay_toggle = BooleanToggleable(default=True)

        super().__init__(input_state_handler=self.define_input_handler())

        self.graph = Graph(graph_dict=large_circuit((0.25 * self.screen_width, 0.75 * self.screen_width),
                                                    (0.25 * self.screen_height, 0.75 * self.screen_height),
                                                    spread=25))
        self.graph_handler = PyGraphHandler(
            screen=self.screen,
            graph_getter=lambda: self.graph,
            route_draw_args_getter=lambda: [g_util.RouteDrawArgs(
               id='Route',
                route=self.get_a_route(),
                edge_draw_args=line_u.DrawLineArgs(
                    color=Color.TEAL,
                    width=3,
                    draw_label_args=lab_u.DrawLabelArgs(
                        color=Color.TEAL
                    ),
                    directionality_draw_args=line_u.DirectionalityIndicatorDrawArgs(
                        color=Color.TEAL
                    )
                ),
                node_draw_args=point_u.DrawPointArgs(
                    color=Color.TEAL,
                    radius=5
                ),
                node_label_args=lab_u.DrawLabelArgs(
                    color=Color.TEAL
                )
            )]
        )

    def on_resize(self):
        self.graph_handler.redraw()

    def define_input_handler(self):
        return InputStateHandler(
            quit_callback_package=CallbackPackage(down=lambda x: self.quit()),
            debug_callback_package=CallbackPackage(down=lambda x: self.debug_mode_toggle.toggle()),
            fullscreen_callback_package=CallbackPackage(down=lambda x: self.toggle_fullscreen()),
            key_actions=[
                InputAction([InputEvent(PyKeys.K_r, event_type=InputEventType.DOWN)], callback=lambda x: self.redraw()),
                InputAction([InputEvent(PyKeys.K_1, event_type=InputEventType.DOWN)], callback=lambda x: self.graph_handler.toggle_surface([GraphSurfaceType.EDGES_SURFACE_ID])),
                InputAction([InputEvent(PyKeys.K_2, event_type=InputEventType.DOWN)], callback=lambda x: self.graph_handler.toggle_surface([GraphSurfaceType.NODES_SURFACE_ID])),
                InputAction([InputEvent(PyKeys.K_3, event_type=InputEventType.DOWN)], callback=lambda x: self.graph_handler.toggle_surface([GraphSurfaceType.EDGE_LABELS_SURFACE_ID])),
                InputAction([InputEvent(PyKeys.K_4, event_type=InputEventType.DOWN)], callback=lambda x: self.graph_handler.toggle_surface([GraphSurfaceType.NODE_LABELS_SURFACE_ID])),
                InputAction([InputEvent(PyKeys.K_5, event_type=InputEventType.DOWN)], callback=lambda x: self.graph_handler.toggle_surface([GraphSurfaceType.OVERLAY_SURFACE_ID])),
                InputAction([InputEvent(PyKeys.K_6, event_type=InputEventType.DOWN)], callback=lambda x: self.graph_handler.toggle_surface([GraphSurfaceType.ROUTES_SURFACE_ID])),
                InputAction([InputEvent(PyKeys.K_KP_PLUS, event_type=InputEventType.DOWN)], callback=lambda x: self.graph_handler.show_all()),
                InputAction([InputEvent(PyKeys.K_KP_MINUS, event_type=InputEventType.DOWN)], callback=lambda x: self.graph_handler.hide_all()),

        ])

    def redraw(self):
        self.graph_handler.redraw()

    def draw(self, frames: int, debug_mode: bool = False):
        self.graph_handler.render(self.screen)

    def get_a_route(self) -> List[Edge]:
        s = rnd.choice(self.graph.nodes)
        e = rnd.choice(self.graph.nodes)

        if s == e \
                or s in self.graph.Sinks \
                or e in self.graph.Sources \
                or s in self.graph.Orphans\
                or e in self.graph.Orphans:
            return []

        waypoints = self.graph.astar(
            start=s,
            end=e
        )

        route = [self.graph.edge_between(waypoints.path[ii - 1], waypoints.path[ii]) for ii in range(1, len(waypoints.path))]

        return route


if __name__ == "__main__":
    g = GraphGame_Basic()
    g.main()