# Copyright (c) 2010-2023 openpyxl

from openpyxl.descriptors.serialisable import Serialisable
from openpyxl.descriptors import (
    Typed,
    Float,
    Integer,
    Bool,
    MinMax,
    Set,
    NoneSet,
    String,
    Alias,
)
from openpyxl.descriptors.excel import Coordinate, Percentage
from openpyxl.descriptors.excel import ExtensionList as OfficeArtExtensionList
from .line import LineProperties

from openpyxl.styles.colors import Color
from openpyxl.xml.constants import DRAWING_NS


class Point2D(Serialisable):

    tagname = "off"
    namespace = DRAWING_NS

    x = Coordinate()
    y = Coordinate()

    def __init__(self,
                 x=None,
                 y=None,
                ):
        self.x = x
        self.y = y


class PositiveSize2D(Serialisable):

    tagname = "ext"
    namespace = DRAWING_NS

    """
    Dimensions in EMUs
    """

    cx = Integer()
    width = Alias('cx')
    cy = Integer()
    height = Alias('cy')

    def __init__(self,
                 cx=None,
                 cy=None,
                ):
        self.cx = cx
        self.cy = cy


class Transform2D(Serialisable):

    tagname = "xfrm"
    namespace = DRAWING_NS

    rot = Integer(allow_none=True)
    flipH = Bool(allow_none=True)
    flipV = Bool(allow_none=True)
    off = Typed(expected_type=Point2D, allow_none=True)
    ext = Typed(expected_type=PositiveSize2D, allow_none=True)
    chOff = Typed(expected_type=Point2D, allow_none=True)
    chExt = Typed(expected_type=PositiveSize2D, allow_none=True)

    __elements__ = ('off', 'ext', 'chOff', 'chExt')

    def __init__(self,
                 rot=None,
                 flipH=None,
                 flipV=None,
                 off=None,
                 ext=None,
                 chOff=None,
                 chExt=None,
                ):
        self.rot = rot
        self.flipH = flipH
        self.flipV = flipV
        self.off = off
        self.ext = ext
        self.chOff = chOff
        self.chExt = chExt


class GroupTransform2D(Serialisable):

    tagname = "xfrm"
    namespace = DRAWING_NS

    rot = Integer(allow_none=True)
    flipH = Bool(allow_none=True)
    flipV = Bool(allow_none=True)
    off = Typed(expected_type=Point2D, allow_none=True)
    ext = Typed(expected_type=PositiveSize2D, allow_none=True)
    chOff = Typed(expected_type=Point2D, allow_none=True)
    chExt = Typed(expected_type=PositiveSize2D, allow_none=True)

    __elements__ = ("off", "ext", "chOff", "chExt")

    def __init__(self,
                 rot=0,
                 flipH=None,
                 flipV=None,
                 off=None,
                 ext=None,
                 chOff=None,
                 chExt=None,
                ):
        self.rot = rot
        self.flipH = flipH
        self.flipV = flipV
        self.off = off
        self.ext = ext
        self.chOff = chOff
        self.chExt = chExt


class SphereCoords(Serialisable):

    tagname = "sphereCoords" # usually

    lat = Integer()
    lon = Integer()
    rev = Integer()

    def __init__(self,
                 lat=None,
                 lon=None,
                 rev=None,
                ):
        self.lat = lat
        self.lon = lon
        self.rev = rev


class Camera(Serialisable):

    tagname = "camera"

    prst = Set(values=[
        'legacyObliqueTopLeft', 'legacyObliqueTop', 'legacyObliqueTopRight', 'legacyObliqueLeft',
         'legacyObliqueFront', 'legacyObliqueRight', 'legacyObliqueBottomLeft',
         'legacyObliqueBottom', 'legacyObliqueBottomRight', 'legacyPerspectiveTopLeft',
         'legacyPerspectiveTop', 'legacyPerspectiveTopRight', 'legacyPerspectiveLeft',
         'legacyPerspectiveFront', 'legacyPerspectiveRight', 'legacyPerspectiveBottomLeft',
         'legacyPerspectiveBottom', 'legacyPerspectiveBottomRight', 'orthographicFront',
         'isometricTopUp', 'isometricTopDown', 'isometricBottomUp', 'isometricBottomDown',
         'isometricLeftUp', 'isometricLeftDown', 'isometricRightUp', 'isometricRightDown',
         'isometricOffAxis1Left', 'isometricOffAxis1Right', 'isometricOffAxis1Top',
         'isometricOffAxis2Left', 'isometricOffAxis2Right', 'isometricOffAxis2Top',
         'isometricOffAxis3Left', 'isometricOffAxis3Right', 'isometricOffAxis3Bottom',
         'isometricOffAxis4Left', 'isometricOffAxis4Right', 'isometricOffAxis4Bottom',
         'obliqueTopLeft',  'obliqueTop', 'obliqueTopRight', 'obliqueLeft', 'obliqueRight',
         'obliqueBottomLeft', 'obliqueBottom', 'obliqueBottomRight', 'perspectiveFront',
         'perspectiveLeft', 'perspectiveRight', 'perspectiveAbove', 'perspectiveBelow',
         'perspectiveAboveLeftFacing', 'perspectiveAboveRightFacing',
         'perspectiveContrastingLeftFacing', 'perspectiveContrastingRightFacing',
         'perspectiveHeroicLeftFacing', 'perspectiveHeroicRightFacing',
         'perspectiveHeroicExtremeLeftFacing', 'perspectiveHeroicExtremeRightFacing',
         'perspectiveRelaxed', 'perspectiveRelaxedModerately'])
    fov = Integer(allow_none=True)
    zoom = Typed(expected_type=Percentage, allow_none=True)
    rot = Typed(expected_type=SphereCoords, allow_none=True)


    def __init__(self,
                 prst=None,
                 fov=None,
                 zoom=None,
                 rot=None,
                ):
        self.prst = prst
        self.fov = fov
        self.zoom = zoom
        self.rot = rot


class LightRig(Serialisable):

    tagname = "lightRig"

    rig = Set(values=['legacyFlat1', 'legacyFlat2', 'legacyFlat3', 'legacyFlat4', 'legacyNormal1',
         'legacyNormal2', 'legacyNormal3', 'legacyNormal4', 'legacyHarsh1',
         'legacyHarsh2', 'legacyHarsh3', 'legacyHarsh4', 'threePt', 'balanced',
         'soft', 'harsh', 'flood', 'contrasting', 'morning', 'sunrise', 'sunset',
         'chilly', 'freezing', 'flat', 'twoPt', 'glow', 'brightRoom']
    )
    dir = Set(values=(['tl', 't', 'tr', 'l', 'r', 'bl', 'b', 'br']))
    rot = Typed(expected_type=SphereCoords, allow_none=True)

    def __init__(self,
                 rig=None,
                 dir=None,
                 rot=None,
                ):
        self.rig = rig
        self.dir = dir
        self.rot = rot


class Vector3D(Serialisable):

    tagname = "vector"

    dx = Integer() # can be in or universl measure :-/
    dy = Integer()
    dz = Integer()

    def __init__(self,
                 dx=None,
                 dy=None,
                 dz=None,
                ):
        self.dx = dx
        self.dy = dy
        self.dz = dz


class Point3D(Serialisable):

    tagname = "anchor"

    x = Integer()
    y = Integer()
    z = Integer()

    def __init__(self,
                 x=None,
                 y=None,
                 z=None,
                ):
        self.x = x
        self.y = y
        self.z = z


class Backdrop(Serialisable):

    anchor = Typed(expected_type=Point3D, )
    norm = Typed(expected_type=Vector3D, )
    up = Typed(expected_type=Vector3D, )
    extLst = Typed(expected_type=OfficeArtExtensionList, allow_none=True)

    def __init__(self,
                 anchor=None,
                 norm=None,
                 up=None,
                 extLst=None,
                ):
        self.anchor = anchor
        self.norm = norm
        self.up = up
        self.extLst = extLst


class Scene3D(Serialisable):

    camera = Typed(expected_type=Camera, )
    lightRig = Typed(expected_type=LightRig, )
    backdrop = Typed(expected_type=Backdrop, allow_none=True)
    extLst = Typed(expected_type=OfficeArtExtensionList, allow_none=True)

    def __init__(self,
                 camera=None,
                 lightRig=None,
                 backdrop=None,
                 extLst=None,
                ):
        self.camera = camera
        self.lightRig = lightRig
        self.backdrop = backdrop
        self.extLst = extLst


class Bevel(Serialisable):

    tagname = "bevel"

    w = Integer()
    h = Integer()
    prst = NoneSet(values=
               ['relaxedInset', 'circle', 'slope', 'cross', 'angle',
                'softRound', 'convex', 'coolSlant', 'divot', 'riblet',
                 'hardEdge', 'artDeco']
               )

    def __init__(self,
                 w=None,
                 h=None,
                 prst=None,
                ):
        self.w = w
        self.h = h
        self.prst = prst


class Shape3D(Serialisable):

    namespace = DRAWING_NS

    z = Typed(expected_type=Coordinate, allow_none=True)
    extrusionH = Integer(allow_none=True)
    contourW = Integer(allow_none=True)
    prstMaterial = NoneSet(values=[
        'legacyMatte','legacyPlastic', 'legacyMetal', 'legacyWireframe', 'matte', 'plastic',
        'metal', 'warmMatte', 'translucentPowder', 'powder', 'dkEdge',
        'softEdge', 'clear', 'flat', 'softmetal']
                       )
    bevelT = Typed(expected_type=Bevel, allow_none=True)
    bevelB = Typed(expected_type=Bevel, allow_none=True)
    extrusionClr = Typed(expected_type=Color, allow_none=True)
    contourClr = Typed(expected_type=Color, allow_none=True)
    extLst = Typed(expected_type=OfficeArtExtensionList, allow_none=True)

    def __init__(self,
                 z=None,
                 extrusionH=None,
                 contourW=None,
                 prstMaterial=None,
                 bevelT=None,
                 bevelB=None,
                 extrusionClr=None,
                 contourClr=None,
                 extLst=None,
                ):
        self.z = z
        self.extrusionH = extrusionH
        self.contourW = contourW
        self.prstMaterial = prstMaterial
        self.bevelT = bevelT
        self.bevelB = bevelB
        self.extrusionClr = extrusionClr
        self.contourClr = contourClr
        self.extLst = extLst


class Path2D(Serialisable):

    w = Float()
    h = Float()
    fill = NoneSet(values=(['norm', 'lighten', 'lightenLess', 'darken', 'darkenLess']))
    stroke = Bool(allow_none=True)
    extrusionOk = Bool(allow_none=True)

    def __init__(self,
                 w=None,
                 h=None,
                 fill=None,
                 stroke=None,
                 extrusionOk=None,
                ):
        self.w = w
        self.h = h
        self.fill = fill
        self.stroke = stroke
        self.extrusionOk = extrusionOk


class Path2DList(Serialisable):

    path = Typed(expected_type=Path2D, allow_none=True)

    def __init__(self,
                 path=None,
                ):
        self.path = path


class GeomRect(Serialisable):

    l = Coordinate()
    t = Coordinate()
    r = Coordinate()
    b = Coordinate()

    def __init__(self,
                 l=None,
                 t=None,
                 r=None,
                 b=None,
                ):
        self.l = l
        self.t = t
        self.r = r
        self.b = b


class AdjPoint2D(Serialisable):

    x = Coordinate()
    y = Coordinate()

    def __init__(self,
                 x=None,
                 y=None,
                ):
        self.x = x
        self.y = y


class ConnectionSite(Serialisable):

    ang = MinMax(min=0, max=360) # guess work, can also be a name
    pos = Typed(expected_type=AdjPoint2D, )

    def __init__(self,
                 ang=None,
                 pos=None,
                ):
        self.ang = ang
        self.pos = pos


class ConnectionSiteList(Serialisable):

    cxn = Typed(expected_type=ConnectionSite, allow_none=True)

    def __init__(self,
                 cxn=None,
                ):
        self.cxn = cxn


class AdjustHandleList(Serialisable):

    pass

class GeomGuide(Serialisable):

    name = String()
    fmla = String()

    def __init__(self,
                 name=None,
                 fmla=None,
                ):
        self.name = name
        self.fmla = fmla


class GeomGuideList(Serialisable):

    gd = Typed(expected_type=GeomGuide, allow_none=True)

    def __init__(self,
                 gd=None,
                ):
        self.gd = gd


class CustomGeometry2D(Serialisable):

    avLst = Typed(expected_type=GeomGuideList, allow_none=True)
    gdLst = Typed(expected_type=GeomGuideList, allow_none=True)
    ahLst = Typed(expected_type=AdjustHandleList, allow_none=True)
    cxnLst = Typed(expected_type=ConnectionSiteList, allow_none=True)
    #rect = Typed(expected_type=GeomRect, allow_none=True)
    pathLst = Typed(expected_type=Path2DList, )

    def __init__(self,
                 avLst=None,
                 gdLst=None,
                 ahLst=None,
                 cxnLst=None,
                 rect=None,
                 pathLst=None,
                ):
        self.avLst = avLst
        self.gdLst = gdLst
        self.ahLst = ahLst
        self.cxnLst = cxnLst
        self.rect = None
        self.pathLst = pathLst


class PresetGeometry2D(Serialisable):

    namespace = DRAWING_NS

    prst = Set(values=(
        ['line', 'lineInv', 'triangle', 'rtTriangle', 'rect',
         'diamond', 'parallelogram', 'trapezoid', 'nonIsoscelesTrapezoid',
         'pentagon', 'hexagon', 'heptagon', 'octagon', 'decagon', 'dodecagon',
         'star4', 'star5', 'star6', 'star7', 'star8', 'star10', 'star12',
         'star16', 'star24', 'star32', 'roundRect', 'round1Rect',
         'round2SameRect', 'round2DiagRect', 'snipRoundRect', 'snip1Rect',
         'snip2SameRect', 'snip2DiagRect', 'plaque', 'ellipse', 'teardrop',
         'homePlate', 'chevron', 'pieWedge', 'pie', 'blockArc', 'donut',
         'noSmoking', 'rightArrow', 'leftArrow', 'upArrow', 'downArrow',
         'stripedRightArrow', 'notchedRightArrow', 'bentUpArrow',
         'leftRightArrow', 'upDownArrow', 'leftUpArrow', 'leftRightUpArrow',
         'quadArrow', 'leftArrowCallout', 'rightArrowCallout', 'upArrowCallout',
         'downArrowCallout', 'leftRightArrowCallout', 'upDownArrowCallout',
         'quadArrowCallout', 'bentArrow', 'uturnArrow', 'circularArrow',
         'leftCircularArrow', 'leftRightCircularArrow', 'curvedRightArrow',
         'curvedLeftArrow', 'curvedUpArrow', 'curvedDownArrow', 'swooshArrow',
         'cube', 'can', 'lightningBolt', 'heart', 'sun', 'moon', 'smileyFace',
         'irregularSeal1', 'irregularSeal2', 'foldedCorner', 'bevel', 'frame',
         'halfFrame', 'corner', 'diagStripe', 'chord', 'arc', 'leftBracket',
         'rightBracket', 'leftBrace', 'rightBrace', 'bracketPair', 'bracePair',
         'straightConnector1', 'bentConnector2', 'bentConnector3',
         'bentConnector4', 'bentConnector5', 'curvedConnector2',
         'curvedConnector3', 'curvedConnector4', 'curvedConnector5', 'callout1',
         'callout2', 'callout3', 'accentCallout1', 'accentCallout2',
         'accentCallout3', 'borderCallout1', 'borderCallout2', 'borderCallout3',
         'accentBorderCallout1', 'accentBorderCallout2', 'accentBorderCallout3',
         'wedgeRectCallout', 'wedgeRoundRectCallout', 'wedgeEllipseCallout',
         'cloudCallout', 'cloud', 'ribbon', 'ribbon2', 'ellipseRibbon',
         'ellipseRibbon2', 'leftRightRibbon', 'verticalScroll',
         'horizontalScroll', 'wave', 'doubleWave', 'plus', 'flowChartProcess',
         'flowChartDecision', 'flowChartInputOutput',
         'flowChartPredefinedProcess', 'flowChartInternalStorage',
         'flowChartDocument', 'flowChartMultidocument', 'flowChartTerminator',
         'flowChartPreparation', 'flowChartManualInput',
         'flowChartManualOperation', 'flowChartConnector', 'flowChartPunchedCard',
         'flowChartPunchedTape', 'flowChartSummingJunction', 'flowChartOr',
         'flowChartCollate', 'flowChartSort', 'flowChartExtract',
         'flowChartMerge', 'flowChartOfflineStorage', 'flowChartOnlineStorage',
         'flowChartMagneticTape', 'flowChartMagneticDisk',
         'flowChartMagneticDrum', 'flowChartDisplay', 'flowChartDelay',
         'flowChartAlternateProcess', 'flowChartOffpageConnector',
         'actionButtonBlank', 'actionButtonHome', 'actionButtonHelp',
         'actionButtonInformation', 'actionButtonForwardNext',
         'actionButtonBackPrevious', 'actionButtonEnd', 'actionButtonBeginning',
         'actionButtonReturn', 'actionButtonDocument', 'actionButtonSound',
         'actionButtonMovie', 'gear6', 'gear9', 'funnel', 'mathPlus', 'mathMinus',
         'mathMultiply', 'mathDivide', 'mathEqual', 'mathNotEqual', 'cornerTabs',
         'squareTabs', 'plaqueTabs', 'chartX', 'chartStar', 'chartPlus']))
    avLst = Typed(expected_type=GeomGuideList, allow_none=True)

    def __init__(self,
                 prst=None,
                 avLst=None,
                ):
        self.prst = prst
        self.avLst = avLst


class FontReference(Serialisable):

    idx = NoneSet(values=(['major', 'minor']))

    def __init__(self,
                 idx=None,
                ):
        self.idx = idx


class StyleMatrixReference(Serialisable):

    idx = Integer()

    def __init__(self,
                 idx=None,
                ):
        self.idx = idx


class ShapeStyle(Serialisable):

    lnRef = Typed(expected_type=StyleMatrixReference, )
    fillRef = Typed(expected_type=StyleMatrixReference, )
    effectRef = Typed(expected_type=StyleMatrixReference, )
    fontRef = Typed(expected_type=FontReference, )

    def __init__(self,
                 lnRef=None,
                 fillRef=None,
                 effectRef=None,
                 fontRef=None,
                ):
        self.lnRef = lnRef
        self.fillRef = fillRef
        self.effectRef = effectRef
        self.fontRef = fontRef