from geoplotlib.layers import BaseLayer
from geoplotlib.core import BatchPainter
import geoplotlib
from geoplotlib.utils import BoundingBox, epoch_to_str
import pyglet

import pandas as pd
import numpy as np

from de2.routing import get_graph, euler_distance, pathfinder, find_percentage_point_in_path as fpp


class PoABLayer(BaseLayer):
    """
    Renders the map.
    """

    __colors__ = {
        "highlight": [255, 255, 0, 100], # yellow
        "nodes": [150, 0, 250, 200],     # purple
        "sailing": [0, 150, 250, 200],   # blue
        "tugging": [150, 250, 0, 200]    # green
    }

    def __init__(self, sim=None, streamer=None):
        self.sim = sim
        self.streamer = streamer
        self.sim_active = False

        self.berths = pd.read_csv("berths.csv")
        # Uncomment the following lines if the berths list should only look at berths used
        # self.berths["used"] = False
        # self.paths = pd.read_csv("2022.csv")
        # self.paths.drop_duplicates(["Locatie van", "Locatie naar"], inplace=True)
        #
        # for _, traj in self.paths.iterrows():
        #     self.berths.loc[self.berths["lpl_id"] == traj["Locatie van"], "used"] = True
        #     self.berths.loc[self.berths["lpl_id"] == traj["Locatie naar"], "used"] = True
        # self.berths = self.berths[self.berths["used"]]
        # self.paths = []

        self.painter = BatchPainter()

        self.vcache = {}  # vessel -> source
        self.pcache = {}  # vessel -> path, dists
        self.tcache = {}  # mmsi -> start time

        self.graph = get_graph()

    def draw_shape(self, x, y):
        """
        Draws the shape on the layer, at a specific location.

        Args:
            x (numeric):    The x-position to draw this shape at.
            y (numeric):    The y-position to draw this shape at.
        """
        self.painter.points(x, y, 5)

    def draw_path(self, path, proj):
        """
        Draws a WSG84 path on the layer.

        Args:
            path:   The path to draw as (x, y) tuples.
            proj:   The projector.
        """
        x = []
        y = []
        for ix in range(len(path) - 1):
            point1 = path[ix]
            point2 = path[ix + 1]
            x.append(point1[0])
            x.append(point2[0])
            y.append(point1[1])
            y.append(point2[1])
        px, py = proj.lonlat_to_screen(np.asarray(x), np.asarray(y))
        vx1 = [e for ei, e in enumerate(px) if ei % 2 == 0]
        vx2 = [e for ei, e in enumerate(px) if ei % 2 == 1]
        vy1 = [e for ei, e in enumerate(py) if ei % 2 == 0]
        vy2 = [e for ei, e in enumerate(py) if ei % 2 == 1]
        self.painter.lines(vx1, vy1, vx2, vy2, width=1.5)

    def draw(self, proj, mouse_x, mouse_y, ui_manager):
        self.painter = BatchPainter()
        tooltips = []

        # self.draw_path([[4.295200916909143, 51.33976669811829], [4.295765234634749, 51.33948916458878], [4.293551595412932, 51.33772327775724], [4.292987285902909, 51.338000801857774], [4.294133417960714, 51.33891513667706], [4.295200916909143, 51.33976669811829]], proj)
        # pts = [(4.29508,51.3404), (4.29472,51.33883), (4.29646,51.33891), (4.297,51.33805), (4.29754,51.33719), (4.29533,51.33968), (4.29813,51.33624), (4.29533,51.33968), (4.29867,51.33538), (4.29893,51.33495), (4.2992,51.33452), (4.29947,51.33409), (4.2953,51.33949), (4.29509,51.33908), (4.29518,51.33883), (4.29475,51.33873), (4.30082,51.33193), (4.29465,51.3387), (4.29473,51.33879), (4.2947,51.33877), (4.29469,51.33877), (4.29468,51.33877), (4.29468,51.33876), (4.29467,51.33876), (4.29467,51.33876), (4.29475,51.33879), (4.29474,51.33879), (4.29474,51.33879), (4.29467,51.33873), (4.29467,51.33872), (4.29467,51.33871), (4.2947,51.33874), (4.2947,51.33874), (4.2947,51.33874), (4.29469,51.33874), (4.29469,51.33874), (4.29469,51.33874), (4.29616,51.33904)]
        # self.draw_path(pts, proj)

        # self.painter.set_color([255, 0, 0, 200])
        #
        # # DRAW THE PoAB GRAPH
        # for v in self.graph.vs:
        #     x, y = proj.lonlat_to_screen(np.asarray([v["x"]]), np.asarray([v["y"]]))
        #     self.draw_shape(x, y)
        # for e in self.graph.es:
        #     src = e.source_vertex
        #     tgt = e.target_vertex
        #     cx1, cy1 = proj.lonlat_to_screen(np.asarray([src["x"]]), np.asarray([src["y"]]))
        #     cx2, cy2 = proj.lonlat_to_screen(np.asarray([tgt["x"]]), np.asarray([tgt["y"]]))
        #     self.painter.lines(cx1, cy1, cx2, cy2, width=2)


        if not self.sim_active:
            ui_manager.status("Press Space to Start Simulation")
        else:
            # Show the simulation time in top right
            # model = self.sim.model
            time = self.streamer.time
            ts = int(time + self.streamer.starting_time)
            # time = model.clock.state["time"]
            # ts = int(time + model.scheduler.starting_time / 1000)
            ui_manager.info("TIME: %s" % epoch_to_str(ts))

            # Get all sailing vessels
            for vessel in self.streamer.vessels:
                if vessel.source is not None and vessel.target is not None:
                    if vessel.task == "sailing":
                        self.painter.set_color(self.__colors__["sailing"])
                    else:
                        self.painter.set_color(self.__colors__["tugging"])

                    # Obtain (and draw) vessel trajectory
                    if vessel.mmsi not in self.vcache or self.vcache[vessel.mmsi] != vessel.source:
                        self.vcache[vessel.mmsi] = vessel.source
                        self.pcache[vessel.mmsi] = pathfinder(self.graph, vessel.source, vessel.target)
                        # self.tcache[vessel.mmsi] = time

                    path, dists = self.pcache[vessel.mmsi]

                    # Compute distance traveled and draw a vessel there
                    tot_distance = vessel.total_distance
                    traveled_approx = tot_distance - vessel.distance_left
                    # delta = time - self.tcache[vessel.mmsi]
                    # traveled_approx += delta * vessel.velocity
                    percentage = traveled_approx / tot_distance

                    if sum(dists) == 0:
                        p1 = p2 = path[0]
                        percentage = 1.
                    else:
                        p1, p2, percentage = fpp(path, dists, percentage)
                    sx, sy = proj.lonlat_to_screen(np.asarray([p1[0]]), np.asarray([p1[1]]))
                    tx, ty = proj.lonlat_to_screen(np.asarray([p2[0]]), np.asarray([p2[1]]))

                    dx = (tx - sx) * percentage
                    dy = (ty - sy) * percentage
                    if euler_distance(sx + dx, sy + dy, mouse_x, mouse_y) < 10:
                        self.painter.set_color(self.__colors__["highlight"])
                        tooltips.append("%s (MMSI: %s)" % (str(vessel.name), str(vessel.mmsi)))
                        tooltips.append("velocity: %.3f m/s" % vessel.velocity)
                        tooltips.append("distance total: %.3f m" % vessel.total_distance)
                        tooltips.append("distance left:  %.3f m" % vessel.distance_left)
                        tooltips.append("ETA: %s" % epoch_to_str(vessel.distance_left / vessel.velocity + ts))

                        self.draw_path(path, proj)
                    self.draw_shape(sx + dx, sy + dy)

        ui_manager.tooltip("\n".join(tooltips))
        self.painter.batch_draw()

    def bbox(self):
        box = [max(self.berths["center_lat"]), max(self.berths["center_lon"]), min(self.berths["center_lat"]), min(self.berths["center_lon"])]
        return BoundingBox(north=box[0], east=box[1], south=box[2], west=box[3])

    def on_key_release(self, key, modifiers):
        if key == pyglet.window.key.SPACE:
            if self.sim is not None:
                self.sim.simulate()
                self.sim_active = True


if __name__ == '__main__':
    from de2.elements import Port
    from pypdevs.simulator import Simulator
    from de2.tracer import Streamer

    port = Port("PoAB", "results-de2/plan.csv")

    streamer = Streamer()

    sim = Simulator(port)
    # sim.setClassicDEVS()
    # sim.setVerbose(None)
    sim.setRealTime(scale=0.0002)
    sim.setCustomTracer("de2.tracer", "TracerPort", [streamer])
    # sim.setTerminationTime(31 * 24 * 60 * 60)  # 14 days
    sim.setTerminationCondition(lambda _, m: m.scheduler.state["index"] >= len(m.scheduler.ivef))

    # sim.simulate()
    layer = PoABLayer(sim, streamer)

    geoplotlib.set_window_size(640, 760)
    geoplotlib.tiles_provider({
        'url': lambda zoom, xtile, ytile: 'https://tile.openstreetmap.org/%d/%d/%d.png' % (zoom, xtile, ytile),
        'tiles_dir': 'mytiles',
        'attribution': 'Map tiles by OpenStreetMap Carto, under CC BY 3.0. Data @ OpenStreetMap contributors.'
    })

    geoplotlib.add_layer(layer)
    geoplotlib.show()

    sim.model.print_statistics()