# Created: 09.03.2016
# Copyright (c) 2016-2018, Manfred Moitzi
# License: MIT License
from ezdxf.legacy import dimension
from ezdxf.lldxf.attributes import DXFAttr, DXFAttributes, DefSubclass, XType
from ezdxf.lldxf.const import DXFInternalEzdxfError
from ezdxf.lldxf.tags import Tags
from .graphics import none_subclass, entity_subclass, ModernGraphicEntity, ExtendedTags

dimension_subclass = DefSubclass('AcDbDimension', {
    'geometry': DXFAttr(2),  # name of pseudo-Block containing the current dimension  entity geometry
    'dimstyle': DXFAttr(3, default='STANDARD'),  # dimension style name
    # The dimension style is stored in Drawing.sections.tables.dimstyles,
    # shortcut Drawings.dimstyles property
    'defpoint': DXFAttr(10, xtype=XType.point3d, default=(0.0, 0.0, 0.0)),  # definition point for all dimension types
    'text_midpoint': DXFAttr(11, xtype=XType.any_point),  # midpoint of dimension text
    'dimtype': DXFAttr(70, default=0),  # Dimension type:
    # Values 0–6 are integer values that represent the dimension type.
    # Values 32, 64, and 128 are bit values, which are added to the integer values
    # (value 32 is always set in R13 and later releases)
    # 0 = Rotated, horizontal, or vertical;
    # 1 = Aligned
    # 2 = Angular;
    # 3 = Diameter;
    # 4 = Radius
    # 5 = Angular 3 point;
    # 6 = Ordinate
    # 32 = Indicates that the block reference (group code 2) is referenced by this dimension only
    # 64 = Ordinate type. This is a bit value (bit 7) used only with integer
    # value 6. If set, ordinate is X-type; if not set, ordinate is Y-type
    # 128 = This is a bit value (bit 8) added to the other group 70 values if
    # the dimension text has been positioned at a user-defined location
    # rather than at the default location
    'attachment_point': DXFAttr(71),  # Attachment point:
    # 1 = Top left; 2 = Top center; 3 = Top right
    # 4 = Middle left; 5 = Middle center; 6 = Middle right
    # 7 = Bottom left; 8 = Bottom center; 9 = Bottom right
    'line_spacing_style': DXFAttr(72, default=1),  # Dimension text line-spacing style (optional):
    # 1 (or missing) = At least (taller characters will override)
    # 2 = Exact (taller characters will not override)
    'line_spacing_factor': DXFAttr(41),  # Dimension text-line spacing factor (optional):
    # Percentage of default (3-on-5) line spacing to be applied. Valid values
    # range from 0.25 to 4.00
    'actual_measurement': DXFAttr(42),  # Actual measurement (optional; read-only value)
    'text': DXFAttr(1),  # Dimension text explicitly entered by the user. Optional;
    # default is the measurement.
    # If null or “<>”, the dimension measurement is drawn as the text,
    # if “ “ (one blank space), the text is suppressed.
    # Anything else is drawn as the text.
    'oblique_angle': DXFAttr(52, default=0),  # Linear dimension types with an oblique angle have an optional group
    # code 52. When added to the rotation angle of the linear dimension (group code 50), it gives the angle of the
    # extension lines (DXF reference error: false subclass AcDbAlignedDimension)
    'text_rotation': DXFAttr(53, default=0),  # The optional group code 53 is the rotation angle of the dimension
    # text away from its default orientation (the direction of the dimension line) (optional)
    'horizontal_direction': DXFAttr(51, default=0),  # All dimension types have an optional 51 group code, which
    # indicates the horizontal direction for the dimension entity. The dimension entity determines the orientation of
    # dimension text and lines for horizontal, vertical, and rotated linear dimensions. This group value is the negative
    # of the angle between the OCS X axis and the UCS X axis. It is always in the XY plane of the OCS
    'extrusion': DXFAttr(210, xtype=XType.point3d, default=(0.0, 0.0, 1.0)),
})

aligned_dimension_subclass = DefSubclass('AcDbAlignedDimension', {
    'insert': DXFAttr(12, xtype=XType.point3d, default=(0.0, 0.0, 0.0)),  # Insertion point for clones of a
    # dimension—Baseline and Continue (in OCS)
    'defpoint2': DXFAttr(13, xtype=XType.point3d, default=(0.0, 0.0, 0.0)),  # Definition point for linear and angular dimensions (in WCS)
    'defpoint3': DXFAttr(14, xtype=XType.point3d, default=(0.0, 0.0, 0.0)),  # Definition point for linear and angular dimensions (in WCS)
    # The defpoint2 (13,23,33) specifies the start point of the first extension line and
    # the defpoint3 (14,24,34) specifies the start point of the second extension line.
    # Defpoint (10,20,30) specifies the dimension line location. The text_midpoint (11,21,31)
    # specifies the midpoint of the dimension text.
    'angle': DXFAttr(50, default=0),  # Angle of rotated, horizontal, or vertical dimensions
})

rotated_dimension_subclass = DefSubclass('AcDbRotatedDimension', {
})

radial_diametric_attribs = {
    'defpoint4': DXFAttr(15, xtype=XType.point3d, default=(0.0, 0.0, 0.0)),  # Definition point for diameter, radius, and angular dimensions (in WCS)
    'leader_length': DXFAttr(40),  # Leader length for radius and diameter dimensions
}

radial_dimension_subclass = DefSubclass('AcDbRadialDimension', radial_diametric_attribs)

diametric_dimension_subclass = DefSubclass('AcDbDiametricDimension', radial_diametric_attribs)

angular_dimension_subclass = DefSubclass('AcDb3dPointAngularDimension', {
    'defpoint2': DXFAttr(13, xtype=XType.point3d, default=(0.0, 0.0, 0.0)),  # Definition point for linear and angular dimensions (in WCS)
    'defpoint3': DXFAttr(14, xtype=XType.point3d, default=(0.0, 0.0, 0.0)),  # Definition point for linear and angular dimensions (in WCS)
    'defpoint4': DXFAttr(15, xtype=XType.point3d, default=(0.0, 0.0, 0.0)),  # Definition point for diameter, radius, and angular dimensions (in WCS)
    'defpoint5': DXFAttr(16, xtype=XType.point3d, default=(0.0, 0.0, 0.0)),  # Point defining dimension arc for angular dimensions (in OCS)
    # The defpoint2 (13,23,33) and defpoint3 (14,24,34) specify the endpoints of the line used to determine the first
    # extension line. Defpoint (10,20,30) and defpoint4 (15,25,35) specify the endpoints of the line used to determine
    # the second extension line. Defpoint5 (16,26,36) specifies the location of the dimension line arc.
    # The text_midpoint (11,21,31) specifies the midpoint of the dimension text.

    # The point (15,25,35) specifies the vertex of the angle. The points (13,23,33)
    # and (14,24,34) specify the endpoints of the extension lines. The point
    # (10,20,30) specifies the location of the dimension line arc and the point
    # (11,21,31) specifies the midpoint of the dimension text.

})

ordinate_dimension_subclass = DefSubclass('AcDbOrdinateDimension', {
    'defpoint2': DXFAttr(13, xtype=XType.point3d, default=(0.0, 0.0, 0.0)),  # Definition point for linear and angular dimensions (in WCS)
    'defpoint3': DXFAttr(14, xtype=XType.point3d, default=(0.0, 0.0, 0.0)),  # Definition point for linear and angular dimensions (in WCS)
    # The defpoint1 (13,23,33) specifies the feature location and the defpoint2 (14,24,34) specifies the leader
    # endpoint. The text_midpoint (11,21,31) specifies the midpoint of the dimension text. Defpoint (10,20,30) is placed
    # at the origin of the UCS that is current when the dimension is created.
})


_DIMTXSTY = 'dimtxsty'

_DIMENSION_TPL = """  0
DIMENSION
5
0
102
{ACAD_REACTORS
330
0
102
}
330
DEAD
100
AcDbEntity
8
0
100
AcDbDimension
2
*D0
10
0.0
20
0.0
30
0.0
11
0.0
21
0.0
31
0.0
70
32
71
5
42
0.0
3
STANDARD
"""

_ALIGNED_TPL = """100
AcDbAlignedDimension
 13
0.0
 23
0.0
 33
0.0
 14
0.0
 24
0.0
 34
0.0
"""

_ROTATED_TPL = """100
AcDbRotatedDimension
"""

_RADIAL_TPL = """100
AcDbRadialDimension
"""

_DIAMETRIC_TPL = """100
AcDbDiametricDimension
"""

_ANGULAR_TPL = """100
AcDb3dPointAngularDimension
"""

_ORDINATE_TPL = """100
AcDbOrdinateDimension
"""


class Dimension(dimension.Dimension, ModernGraphicEntity):
    __slots__ = ()
    BLOCK_EXCLUSIVE = 32
    TEMPLATE = ExtendedTags.from_text(_DIMENSION_TPL)
    DXFATTRIBS = DXFAttributes(none_subclass, entity_subclass, dimension_subclass)

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

    def add_subclasses(self) -> None:
        def add_subclasses(templates) -> None:
            for template in templates:
                self.tags.subclasses.append(Tags.from_text(template))
        dim_type = self.dim_type
        if dim_type == self.LINEAR:
            add_subclasses([_ALIGNED_TPL, _ROTATED_TPL])
        elif dim_type == self.ALIGNED:
            add_subclasses([_ALIGNED_TPL])
        elif dim_type in (self.ANGULAR, self.ANGULAR_3P):
            add_subclasses([_ANGULAR_TPL])
        elif dim_type == self.RADIUS:
            add_subclasses([_RADIAL_TPL])
        elif dim_type == self.DIAMETER:
            add_subclasses([_DIAMETRIC_TPL])
        elif dim_type == self.ORDINATE:
            add_subclasses([_ORDINATE_TPL])

    def cast(self) -> 'Dimension':  # create the REAL dimension entity
        DimClass = DimensionClasses[self.dim_type]
        return DimClass(self.tags, self.drawing)

    def set_acad_dstyle(self, data: dict) -> None:
        if self.drawing is None:
            raise DXFInternalEzdxfError('Dimension.drawing attribute not initialized.')
        # replace virtual 'dimtxsty' attribute by 'dimtxsty_handle'
        if _DIMTXSTY in data:
            dimtxsty = data.pop(_DIMTXSTY)
            txtstyle = self.drawing.styles.get(dimtxsty)
            data['dimtxsty_handle'] = txtstyle.dxf.handle
        super().set_acad_dstyle(data)


class AlignedDimension(Dimension):
    __slots__ = ()
    DXFATTRIBS = DXFAttributes(none_subclass, entity_subclass, dimension_subclass, aligned_dimension_subclass)


class RotatedDimension(Dimension):
    __slots__ = ()
    DXFATTRIBS = DXFAttributes(none_subclass, entity_subclass, dimension_subclass, aligned_dimension_subclass,
                               rotated_dimension_subclass)


class RadialDimension(Dimension):
    __slots__ = ()
    DXFATTRIBS = DXFAttributes(none_subclass, entity_subclass, dimension_subclass, radial_dimension_subclass)


class DiametricDimension(Dimension):
    __slots__ = ()
    DXFATTRIBS = DXFAttributes(none_subclass, entity_subclass, dimension_subclass, diametric_dimension_subclass)


class AngularDimension(Dimension):
    __slots__ = ()
    DXFATTRIBS = DXFAttributes(none_subclass, entity_subclass, dimension_subclass, angular_dimension_subclass)


class OrdinateDimension(Dimension):
    __slots__ = ()
    DXFATTRIBS = DXFAttributes(none_subclass, entity_subclass, dimension_subclass, ordinate_dimension_subclass)


DimensionClasses = [
    RotatedDimension,  # 0
    AlignedDimension,  # 1
    AngularDimension,  # 2
    DiametricDimension,  # 3
    RadialDimension,  # 4
    AngularDimension,  # 5
    OrdinateDimension,  # 6
]
