from collections import deque
from itertools import chain

import numpy as np

from c3nav.mapdata.models import AltitudeArea
from c3nav.mapdata.models.geometry.level import AltitudeAreaPoint
from c3nav.mapdata.render.geometry.hybrid import HybridGeometry


class AltitudeAreaGeometries:
    def __init__(self, altitudearea=None, colors=None, obstacles=None):
        if altitudearea is not None:
            self.geometry = altitudearea.geometry
            self.altitude = None if altitudearea.altitude is None else int(altitudearea.altitude * 1000)
            self.points = None if altitudearea.points is None else [AltitudeAreaPoint(coordinates=p.coordinates,
                                                                                      altitude=int(p.altitude*2))
                                                                    for p in altitudearea.points]
        else:
            self.geometry = None
            self.altitude = None
            self.points = None
        self.base = None
        self.bottom = None
        self.colors = colors
        self.obstacles = obstacles

    @property
    def min_altitude(self) -> float:
        return self.altitude if self.altitude is not None else min(p.altitude for p in self.points)

    @property
    def max_altitude(self) -> float:
        return self.altitude if self.altitude is not None else max(p.altitude for p in self.points)

    def get_altitudes(self, points):
        # noinspection PyCallByClass,PyTypeChecker
        return AltitudeArea.get_altitudes(self, points/1000).astype(np.int32)

    def create_hybrid_geometries(self, face_centers, vertices_offset, faces_offset):
        self.geometry = HybridGeometry.create(self.geometry, face_centers)

        vertices = deque()
        faces = deque()

        for color, areas in self.colors.items():
            for height in tuple(areas.keys()):
                faces_offset, vertices_offset = self._call_create_full(areas, height, faces, vertices,
                                                                       faces_offset, vertices_offset)

        for height_obstacles in self.obstacles.values():
            for color_obstacles in height_obstacles.values():
                for i in range(len(color_obstacles)):
                    faces_offset, vertices_offset = self._call_create_full(color_obstacles, i, faces, vertices,
                                                                           faces_offset, vertices_offset)

        if not vertices:
            return np.empty((0, 2), dtype=np.int32), np.empty((0, 3), dtype=np.uint32)
        return np.vstack(vertices), np.vstack(faces)

    def _call_create_full(self, mapping, key, faces, vertices, faces_offset, vertices_offset):
        geom = mapping[key]
        new_geom, new_vertices, new_faces = HybridGeometry.create_full(geom, vertices_offset, faces_offset)
        mapping[key] = new_geom
        vertices_offset += new_vertices.shape[0]
        faces_offset += new_faces.shape[0]
        vertices.append(new_vertices)
        faces.append(new_faces)
        return faces_offset, vertices_offset

    def remove_faces(self, faces):
        self.geometry.remove_faces(faces)
        for areas in self.colors.values():
            for area in areas.values():
                area.remove_faces(faces)

    def create_polyhedrons(self, create_polyhedron, altitudes, min_altitude, crops):
        if self.points is None:
            altitudes = self.altitude

        self.base = HybridGeometry(self.geometry.geom, self.geometry.faces)
        self.bottom = HybridGeometry(self.geometry.geom, self.geometry.faces)
        self.geometry.build_polyhedron(create_polyhedron,
                                       lower=altitudes - int(0.7 * 1000),
                                       upper=altitudes,
                                       crops=crops)
        self.base.build_polyhedron(create_polyhedron,
                                   lower=min_altitude - int(0.7 * 1000),
                                   upper=altitudes - int(0.7 * 1000),
                                   crops=crops,
                                   top=False, bottom=False)
        self.bottom.build_polyhedron(create_polyhedron,
                                     lower=0, upper=1,
                                     crops=crops,
                                     top=False)

        for geometry in chain(*(areas.values() for areas in self.colors.values())):
            geometry.build_polyhedron(create_polyhedron,
                                      lower=altitudes,
                                      upper=altitudes + int(0.001 * 1000),
                                      crops=crops)
        # todo: treat altitude properly
        for height, height_geometries in self.obstacles.items():
            for color, color_geometries in height_geometries.items():
                for geometry in color_geometries:
                    geometry.build_polyhedron(create_polyhedron,
                                              lower=altitudes,
                                              upper=altitudes + height,
                                              crops=crops)
