# Created: 25.03.2011
# Copyright (c) 2011-2018, Manfred Moitzi
# License: MIT License
from typing import TYPE_CHECKING, Optional
from ezdxf.lldxf.types import DXFTag
from ezdxf.lldxf.tags import Tags
from ezdxf.lldxf import loader
from ezdxf.legacy import polyline
from ezdxf.legacy.facemixins import PolyfaceMixin, PolymeshMixin

from .graphics import ExtendedTags, DXFAttr, DefSubclass, DXFAttributes, XType
from .graphics import none_subclass, entity_subclass, ModernGraphicEntityExtension

if TYPE_CHECKING:
    from ezdxf.eztypes import Tags

_POLYLINE_TPL = """0
POLYLINE
5
0
330
0
100
AcDbEntity
8
0
100
AcDb2dPolyline
66
1
70
0
10
0.0
20
0.0
30
0.0
"""

polyline_subclass = DefSubclass('AcDb2dPolyline', {
    'elevation': DXFAttr(10, xtype=XType.point3d),
    'flags': DXFAttr(70, default=0),
    'default_start_width': DXFAttr(40, default=0.0),
    'default_end_width': DXFAttr(41, default=0.0),
    'm_count': DXFAttr(71, default=0),
    'n_count': DXFAttr(72, default=0),
    'm_smooth_density': DXFAttr(73, default=0),
    'n_smooth_density': DXFAttr(74, default=0),
    'smooth_type': DXFAttr(75, default=0),
    'thickness': DXFAttr(39, default=0.0),
    'extrusion': DXFAttr(210, xtype=XType.point3d, default=(0.0, 0.0, 1.0)),
})


class Polyline(polyline.Polyline, ModernGraphicEntityExtension):
    __slots__ = ()
    TEMPLATE = ExtendedTags.from_text(_POLYLINE_TPL)
    DXFATTRIBS = DXFAttributes(none_subclass, entity_subclass, polyline_subclass)

    def post_new_hook(self) -> None:
        super(Polyline, self).post_new_hook()
        self.update_subclass_specifier()

    def update_subclass_specifier(self) -> None:
        # For dxf attribute access not the name of the subclass is important, but
        # the order of the subclasses 1st, 2nd, 3rd and so on.
        # The 3rd subclass is the AcDb3dPolyline or AcDb2dPolyline subclass
        subclass = self.tags.subclasses[2]  # type: Tags
        subclass[0] = DXFTag(100, self.get_mode())

    def cast(self) -> 'Polyline':
        mode = self.get_mode()
        if mode == 'AcDbPolyFaceMesh':
            return Polyface.convert(self)
        elif mode == 'AcDbPolygonMesh':
            return Polymesh.convert(self)
        else:
            return self


class Polyface(Polyline, PolyfaceMixin):
    __slots__ = ()
    """
    PolyFace structure:

    POLYLINE
      AcDbEntity
      AcDbPolyFaceMesh
    VERTEX - Vertex
      AcDbEntity
      AcDbVertex
      AcDbPolyFaceMeshVertex
    VERTEX - Face
      AcDbEntity
      AcDbFaceRecord
    SEQEND

    """
    @staticmethod
    def convert(polyline: 'Polyline') -> 'Polyface':
        return Polyface(polyline.tags, polyline.drawing)


class Polymesh(Polyline, PolymeshMixin):
    __slots__ = ()
    """
    PolyMesh structure:

    POLYLINE
      AcDbEntity
      AcDbPolygonMesh
    VERTEX
      AcDbEntity
      AcDbVertex
      AcDbPolygonMeshVertex
    """
    @staticmethod
    def convert(polyline: 'Polyline') -> 'Polymesh':
        return Polymesh(polyline.tags, polyline.drawing)


_VERTEX_TPL = """0
VERTEX
5
0
330
0
100
AcDbEntity
8
0
100
AcDbVertex
100
AcDb2dVertex
10
0.0
20
0.0
30
0.0
70
0
"""
vertex_subclass = (
    DefSubclass('AcDbVertex', {}),  # subclasses[2]
    DefSubclass('AcDb2dVertex', {  # subclasses[3]
        'location': DXFAttr(10, xtype=XType.any_point),
        'start_width': DXFAttr(40, default=0.0),
        'end_width': DXFAttr(41, default=0.0),
        'bulge': DXFAttr(42, default=0),
        'flags': DXFAttr(70),
        'tangent': DXFAttr(50),
        'vtx0': DXFAttr(71),
        'vtx1': DXFAttr(72),
        'vtx2': DXFAttr(73),
        'vtx3': DXFAttr(74),
    })
)


EMPTY_VERTEX_SUBCLASS = Tags()


class DXFVertex(polyline.DXFVertex, ModernGraphicEntityExtension):
    __slots__ = ()
    TEMPLATE = ExtendedTags.from_text(_VERTEX_TPL)
    DXFATTRIBS = DXFAttributes(none_subclass, entity_subclass, *vertex_subclass)

    def post_new_hook(self) -> None:
        self.update_subclass_specifier()

    def update_subclass_specifier(self) -> None:
        def set_subclass(subclassname: str) -> None:
            subclass = self.tags.subclasses[3]
            subclass[0] = DXFTag(100, subclassname)

        if self.is_3d_polyline_vertex:  # flags & const.VTX_3D_POLYLINE_VERTEX
            set_subclass('AcDb3dPolylineVertex')
        elif self.is_face_record:  # (flags & Vertex.FACE_FLAGS) == const.VTX_3D_POLYFACE_MESH_VERTEX:
            set_subclass('AcDbFaceRecord')
            self.tags.subclasses[2] = EMPTY_VERTEX_SUBCLASS  # clear subclass AcDbVertex
        elif self.is_poly_face_mesh_vertex:  # flags & Vertex.FACE_FLAGS == Vertex.FACE_FLAGS:
            set_subclass('AcDbPolyFaceMeshVertex')
        elif self.is_polygon_mesh_vertex:  # flags & const.VTX_3D_POLYGON_MESH_VERTEX:
            set_subclass('AcDbPolygonMeshVertex')
        else:
            set_subclass('AcDb2dVertex')


@loader.register('VERTEX', legacy=False)
def vertex_tags_processor(tags: ExtendedTags) -> Optional[ExtendedTags]:
    """
    If subclass[2] is not 'AcDbVertex', insert empty subclass

    """
    if not isinstance(tags, ExtendedTags):
        return
    if tags.subclasses[2][0].value != 'AcDbVertex':
        tags.subclasses.insert(2, EMPTY_VERTEX_SUBCLASS)
    return tags
