from UM.Mesh.MeshBuilder import MeshBuilder

import numpy


class Layer:
    def __init__(self, layer_id):
        self._id = layer_id
        self._height = 0.0
        self._thickness = 0.0
        self._polygons = []
        self._element_count = 0

    @property
    def height(self):
        return self._height

    @property
    def thickness(self):
        return self._thickness

    @property
    def polygons(self):
        return self._polygons

    @property
    def elementCount(self):
        return self._element_count

    def setHeight(self, height):
        self._height = height

    def setThickness(self, thickness):
        self._thickness = thickness

    def lineMeshVertexCount(self):
        result = 0
        for polygon in self._polygons:
            result += polygon.lineMeshVertexCount()

        return result

    def lineMeshElementCount(self):
        result = 0
        for polygon in self._polygons:
            result += polygon.lineMeshElementCount()

        return result

    def build(self, vertex_offset, index_offset, vertices, colors, line_dimensions, feedrates, extruders, line_types, indices):
        result_vertex_offset = vertex_offset
        result_index_offset = index_offset
        self._element_count = 0
        for polygon in self._polygons:
            polygon.build(result_vertex_offset, result_index_offset, vertices, colors, line_dimensions, feedrates, extruders, line_types, indices)
            result_vertex_offset += polygon.lineMeshVertexCount()
            result_index_offset += polygon.lineMeshElementCount()
            self._element_count += polygon.elementCount

        return (result_vertex_offset, result_index_offset)

    def createMesh(self):
        return self.createMeshOrJumps(True)

    def createJumps(self):
        return self.createMeshOrJumps(False)

    # Defines the two triplets of local point indices to use to draw the two faces for each line segment in createMeshOrJump
    __index_pattern = numpy.array([[0, 3, 2, 0, 1, 3]], dtype = numpy.int32 )

    def createMeshOrJumps(self, make_mesh):
        builder = MeshBuilder()
        
        line_count = 0
        if make_mesh:
            for polygon in self._polygons:
                line_count += polygon.meshLineCount
        else:
            for polygon in self._polygons:
                line_count += polygon.jumpCount

        # Reserve the neccesary space for the data upfront
        builder.reserveFaceAndVertexCount(2 * line_count, 4 * line_count)
        
        for polygon in self._polygons:
            # Filter out the types of lines we are not interesed in depending on whether we are drawing the mesh or the jumps.
            index_mask = numpy.logical_not(polygon.jumpMask) if make_mesh else polygon.jumpMask

            # Create an array with rows [p p+1] and only keep those we whant to draw based on make_mesh
            points = numpy.concatenate((polygon.data[:-1], polygon.data[1:]), 1)[index_mask.ravel()]
            # Line types of the points we want to draw
            line_types = polygon.types[index_mask]
            
            # Shift the z-axis according to previous implementation.
            if make_mesh:
                points[polygon.isInfillOrSkinType(line_types), 1::3] -= 0.01
            else:
                points[:, 1::3] += 0.01

            # Create an array with normals and tile 2 copies to match size of points variable
            normals = numpy.tile( polygon.getNormals()[index_mask.ravel()], (1, 2))

            # Scale all normals by the line width of the current line so we can easily offset.
            normals *= (polygon.lineWidths[index_mask.ravel()] / 2)

            # Create 4 points to draw each line segment, points +- normals results in 2 points each.
            # After this we reshape to one point per line.
            f_points = numpy.concatenate((points-normals, points+normals), 1).reshape((-1, 3))

            # __index_pattern defines which points to use to draw the two faces for each lines egment, the following linesegment is offset by 4
            f_indices = ( self.__index_pattern + numpy.arange(0, 4 * len(normals), 4, dtype=numpy.int32).reshape((-1, 1)) ).reshape((-1, 3))
            f_colors = numpy.repeat(polygon.mapLineTypeToColor(line_types), 4, 0)

            builder.addFacesWithColor(f_points, f_indices, f_colors)
        
        return builder.build()