# This file is part of the Coral project.
# Please read the copyright notice in the LICENSE file.

import coral
from coral.modeler.diagram.presentation import *
DI = coral.lang.XMIDI20
L = coral.lang.UML14

import qt

class PackageLAY( layout.VBoxLayouterMaster ):
    def moveContained(self, sizeHinter):
        x,y=0,0
        for c in self.visibleContained():
            c.move(x,y)
            y+=c.getSize()[1] - 1

    def layout(self, sizeHinter,
               toMinimal=False, w_parent = -1, h_parent = -1 ):
        w, h = sizeHinter.getSizeHint(self, toMinimal)

##        if w_parent > 0:
##            w = w_parent
##        if h_parent > 0:
##            h = h_parent

        self.resize(w, h)
        nc_w, nc_h = 0,0
        
        for c in self.visibleContained():
            if isinstance(c, NameCompartmentLAY):
                c.layout(sizeHinter, toMinimal)
                nc_w, nc_h = c.getSize()
                    

        for c in self.visibleContained():
            if isinstance(c, BodyCompartmentLAY):
                _w, _h = self.getSize()
                c.layout(sizeHinter,
                         toMinimal, w_parent = _w, h_parent = _h - nc_h)
            
        self.moveContained(sizeHinter)

class ComponentLAY( layout.VBoxLayouterMaster ):
    def minSize(self):
        return (120, 80)

    def padX(self):
        return 30

    def layout(self, sizeHinter,
               toMinimal=False, w_parent = -1, h_parent = -1 ):
        w, h = sizeHinter.getSizeHint(self, toMinimal)

        self.resize(w, h)
        nc_w, nc_h = 0,0

        x_2 = self.padX() / 2
        for c in self.visibleContained():
            if isinstance(c, NameCompartmentLAY):
                nc_w, nc_h = c.getSize()
                c.layout(sizeHinter,
                         True,
                         w_parent = self.getSize()[0] - x_2,
                         h_parent = 30 )
                    
        for c in self.visibleContained():
            if not isinstance(c, NameCompartmentLAY):
                _w, _h = self.getSize()
                c.layout(sizeHinter,
                         toMinimal,
                         w_parent = _w - x_2,
                         h_parent = _h - nc_h)
            
        self.moveContained(sizeHinter)

class ClassLAY( layout.VBoxLayouterMaster ):
    pass

class ArrowLAY( layout.VBoxLayouterMaster ):
    def sizeHint(self, sizeHinter, toMinimal):
        return 20,20

class CompartmentLAY( layout.VBoxLayouter ):
    def minSize(self):
        return 0, 12

class PaddedHBoxLayouterMinChild(layout.HBoxLayouterMinChild):
    def padX(self):
        return 8
    


class SpecialStateLAY( layout.VBoxLayouterMaster ):

    def __isJoinOrFork(self):
        ge = self.graphElement
        if ge:
            sm = ge.semanticModel
            if sm.oclIsKindOf(DI.Uml1SemanticModelBridge):
                if sm.element.oclIsKindOf(L.Pseudostate):
                    if sm.element.kind == "join" or sm.element.kind == "fork":
                        return True
        return False

    def sizeHint(self, sizeHinter, toMinimal):

        if self.__isJoinOrFork():
            return (50, 10)
        else:
            return (20,20)
        
    def moveContained(self, sizeHinter):
        wS, hS = self.getSize()
        

        nl = None

        for c in self.visibleContained():
            if isinstance(c, layout.StringLAY):
                nl = c
        
        if nl:
            wN, hN = nl.getSize()

            a = (wS-wN)/2
            b = hS + 7
            
            nl.move(a,b)

            a = (wS-wN)/2
            b = hS + 7

            nl.move(a,b)

    def layout(self, sizeHinter,
               toMinimal=False , w_parent = -1, h_parent = -1 ):
        ## Layouting of fork & join:
        ## The size is (X,10) or (10,X) where X >= 50
        wS, hS = self.getSize()
        wSH, hSH = sizeHinter.getSizeHint(self, toMinimal)
        if toMinimal:
            if hS > wS and self.__isJoinOrFork():
                wS,hS = hS,wS
                wSH, hSH = hSH, wSH
            self.resize(wSH, hSH)
        else:
            if self.__isJoinOrFork():
                if wS >= hS:
                    self.resize(max(wS,wSH,50), 10)
                else:
                    self.resize(10, max(hS, hSH, 50))
            else:
                self.resize(max(wS,wSH,hSH,hS),max(wS,wSH,hSH,hS))
        for c in self.visibleContained():
            c.layout(sizeHinter, toMinimal)
        self.moveContained(sizeHinter)

class StateLAY( CompartmentLAY ):

    def moveContained(self, sizeHinter):
        x,y=0,0
        ncl = None
        csl = None
        rcl = None

        components = []

        for c in self.visibleContained():
            if isinstance(c, NameCompartmentLAY):
                ncl = c
            elif isinstance(c, layout.HLineLAY):
                csl = c
            else:
                if not c.graphElement.semanticModel.oclIsKindOf(DI.Uml1SemanticModelBridge):
                    components.append(c)
        if ncl:
            ncl.move(x, y)
            y += ncl.getSize()[1]
        if csl:
            csl.move(x, y)
            y += csl.getSize()[1]

        for comp in components:
            comp.move(x,y)

    
    def layout(self, sizeHinter,
               toMinimal=False, w_parent = -1, h_parent = -1 ):
        w, h = sizeHinter.getSizeHint(self, toMinimal)

        if w_parent > 0:
            w = w_parent
        if h_parent > 0:
            h = h_parent

        self.resize(w, h)
        nc_w, nc_h = 0,0
        
        for c in self.visibleContained():
            if isinstance(c, NameCompartmentLAY):
                nc_w, nc_h = c.getSize()
                c.layout(sizeHinter, True, w_parent = self.getSize()[0], h_parent = 30 )

        for c in self.visibleContained():
            _w, _h = self.getSize()
            if isinstance(c, RegionCompartmentLAY):
                c.layout(sizeHinter,
                         toMinimal, w_parent = _w, h_parent = _h - nc_h)
            elif isinstance(c, layout.HLineLAY):
                c.layout(sizeHinter, toMinimal, w_parent = _w)
            else:
                if c.graphElement.semanticModel.oclIsKindOf(DI.Uml1SemanticModelBridge):
                    c.layout(sizeHinter, toMinimal)
                else:
                    c.layout(sizeHinter,
                             toMinimal,
                             w_parent = _w,
                             h_parent = _h - nc_h) 
            
        self.moveContained(sizeHinter)


    def sizeHint(self, sizeHinter, toMinimal):
        w, h = (100, 40)
        if not toMinimal:
            _w, _h = self.getSize()
            ok = False
            try:
                if self.graphElement.__parent__().semanticModel.typeInfo == "RegionCompartment":
                    ok = True
            except:
                pass
            # This makes regions slaves in the horizontal direction
            if not ok:
                w = max(w, _w)
            # This makes regions non-slaves in the horizontal direction
            h = max(h, _h)
            # If we want regions to appear successively in either direction
            # we need to check for it above.

        for c in self.visibleContained():
            if isinstance(c, NameCompartmentLAY):
                _w, _h = sizeHinter.getSizeHint(c, True)
                w = max(w, _w)
                h = max(h, _h)

        ge = self.graphElement
        if ge:
            sm = ge.semanticModel
            if sm.oclIsKindOf(DI.Uml1SemanticModelBridge):
                if sm.element.oclIsKindOf(L.CompositeState):
                    #if sm.element.isConcurrent: ???
                    if 1:
                        for c in self.visibleContained():
                            _w, _h = sizeHinter.getSizeHint(c, toMinimal)
                            _x, _y = c.getPosition()
                            w = max(w, _x + _w)
                            h = max(h, _y + _h)
                        return w, h

        return w, h
                

class NameCompartmentLAY( CompartmentLAY ):

    def moveContained(self, sizeHinter):
        x,y=0,4
        for c in self.visibleContained():
            x=(self.getSize()[0] - sizeHinter.getSizeHint(c, True)[0])/2
            c.move(x,y)
            y+=c.getSize()[1]

    def resize(self,w,h):
        self.graphElement.size.width = max(w,20)
        self.graphElement.size.height = h

    def layout(self, sizeHinter,
               toMinimal=False, w_parent = -1, h_parent = -1 ):
        w, h = sizeHinter.getSizeHint(self, toMinimal)

        if w_parent > 0:
            w = w_parent

        self.resize(w, h)
        for c in self.visibleContained():
            if isinstance(c, layout.HBoxLayouterMinChild):
                c.layout(sizeHinter, True)
            else:
                c.layout(sizeHinter, toMinimal, w_parent =w)
        self.moveContained(sizeHinter)

    def sizeHint(self, sizeHinter, toMinimal):
        hc = 0
        w = 30 #Sentinel value
        for c in self.visibleContained():
            _w, _h = sizeHinter.getSizeHint(c, toMinimal)
            hc += _h
            w = max(_w, w)

        return (w+4, max(hc,12)+6)
    
    

class RegionCompartmentLAY( CompartmentLAY ):

    def layout(self, sizeHinter,
               toMinimal=False, w_parent = -1, h_parent = -1 ):
        w, h = sizeHinter.getSizeHint(self, toMinimal)

        if w_parent > 0:
            w = w_parent
        if h_parent > 0:
            h = h_parent

        self.resize(w, h)

        for c in self.visibleContained():
            c.layout(sizeHinter, toMinimal, w_parent = w)
        self.moveContained(sizeHinter)
            
    def moveContained(self, sizeHinter):

        ## This is a special moveContained, that moves the children
        ## visually correct, and not by index
        
        x,y=0,0

        for c in self.visibleContained():
            c.move(x,y)
            y+=c.getSize()[1]

        
    def sizeHint(self, sizeHinter, toMinimal):
        hc = 0
        w = 100 # Sentinel value
        for c in self.visibleContained():
            _w, _h = sizeHinter.getSizeHint(c, toMinimal)
            hc += _h
            w = max(_w, w)
        
               
        return (w, max(hc,80))
    


class BodyCompartmentLAY( layout.DiagramElementLayouter ):
    def minSize(self):
        return 20, 40

    def padX(self):
        return 5

    def padY(self):
        return 5

    def isSlave(self):
        return True
    
class ExpressionLAY( layout.HBoxLayouter ):
    pass

class AssociationEndLAY( layout.DiagramElementLayouter ):

    def sizeHint(self, sizeHinter, toMinimal):
        return (0,0)

    def layout(self, sizeHinter,
               toMinimal=False, w_parent = -1, h_parent = -1 ):
        self.resize(0, 0)
        for c in self.visibleContained():
            c.layout(sizeHinter, toMinimal)
        self.moveContained(sizeHinter)

    def moveContained(self, sizeHinter):
        assend = self.graphElement
        assge = self.graphElement.container
        part = assend.semanticModel.element.participant
        if not part:
            return
        partxmicand = part.xmidi_get_graphelements()
        if not partxmicand:
            return
        partxmi = None
        
        for a in assge.anchor:
            if a.graphElement in partxmicand:
                partxmi = a.graphElement
                break
            
        name = None
        vis = None
        mult = None
        for c in self.visibleContained():
            sm =  c.graphElement.semanticModel
            if sm:
                if str(sm.typeInfo) == 'Name':
                    name = c.graphElement
                if str(sm.typeInfo) == 'Visibility':
                    vis = c.graphElement     
                if str(sm.typeInfo) == 'Multiplicity':
                    mult = c.graphElement

        if partxmi:
            px = partxmi.position.x
            py = partxmi.position.y
            pw = partxmi.size.width
            ph = partxmi.size.height

            ax = assend.position.x
            ay = assend.position.y

            # The association is fully on the right hand side of the class
            if ax >= px+pw-1 and (ay >= py and ay < ph+py):
                if vis and name:
                    vis.position.x = 15
                    vis.position.y = -20
                    name.position.x = vis.size.width + vis.position.x
                    name.position.y = vis.position.y
                if not vis and name:
                    name.position.x = 15
                    name.position.y = -20
                if mult:
                    mult.position.x = 15
                    mult.position.y = 10
            # The association is fully on the left hand side of the class
            elif ax <= px and (ay >= py and ay < ph+py):
                if vis and name:
                    vis.position.x = -15 - vis.size.width - name.size.width
                    vis.position.y = -20
                    name.position.x = vis.size.width + vis.position.x
                    name.position.y = vis.position.y
                if not vis and name:
                    name.position.x = -15 - name.size.width
                    name.position.y = -20
                if mult:
                    mult.position.x = -15 - mult.size.width
                    mult.position.y = 10
            # The association is fully above the class
            elif (ax >= px and ax < px+pw-1) and ay == py:
                if vis and name:
                    vis.position.x = 15
                    vis.position.y = -15 - max(vis.size.height, name.size.height)
                    name.position.x = vis.size.width + vis.position.x
                    name.position.y = vis.position.y
                if not vis and name:
                    name.position.x = 15
                    name.position.y = -15 - name.size.height
                if mult:
                    mult.position.x = -15 - mult.size.width
                    mult.position.y = -15 - mult.size.height
            # The association is fully below the class
            elif ax >= px and (ay >= py and ay >= ph+py-1):
                if vis and name:
                    vis.position.x = 15
                    vis.position.y = 15
                    name.position.x = vis.size.width + vis.position.x
                    name.position.y = vis.position.y
                if not vis and name:
                    name.position.x = 15
                    name.position.y = 15
                if mult:
                    mult.position.x = -15 - mult.size.width
                    mult.position.y = 15 

class ObjectLAY(layout.VBoxLayouterMaster):
    # BUGGY at least for SequenceDiagrams

    def layout(self, sizeHinter,
               toMinimal=False, w_parent = -1, h_parent = -1 ):
        d = DISUPP.findDiagramOfElement(self.graphElement)
        if d and d.semanticModel.typeInfo == "SequenceDiagram":
            return
        layout.VBoxLayouterMaster.layout(self, sizeHinter,
                                         toMinimal, w_parent, h_parent)

class ActivationLAY(layout.DiagramElementLayouter):
    # BUGGY
    def sizeHint(self, sizeHinter, toMinimal):
        return self.getSize()

    def layout(self, sizeHinter,
               toMinimal=False, w_parent = -1, h_parent = -1 ):
        pass
#
# Edge layouters
#

class AssociationLAY( layout.GraphEdgeLayouter ):
    def moveLabels(self, sizeHinter, startDiff, endDiff):
        """
        If the ends have moved different distances then try to move
        labels relative to their ends..
        """
        # contained[0] is at the start
        # contained[1] is at the end
        # contained[2] is the label on the middle
        edge=self.graphElement
        ##assert len(edge.contained)==3
        


        if len(edge.waypoints) >= 2:
            edge.contained[0].position.x = edge.waypoints[0].x
            edge.contained[0].position.y = edge.waypoints[0].y

            edge.contained[1].position.x = edge.waypoints[-1].x
            edge.contained[1].position.y = edge.waypoints[-1].y

            edge.contained[0].layouter.__getPyValue__().layout(sizeHinter)
            edge.contained[1].layouter.__getPyValue__().layout(sizeHinter)
            
            middleDiff=((startDiff[0]+endDiff[0])/2,(startDiff[1]+endDiff[1])/2)
            if len(edge.contained)>2:
                e=0
                found= False
                for n in edge.contained:
                    if n.semanticModel and n.semanticModel.oclIsKindOf(DI.SimpleSemanticModelElement) and  n.semanticModel.typeInfo=="Name":
                        found=True
                        break
                    e = e+1
                if found:
                    edge.contained[e].position.x += middleDiff[0]
                    edge.contained[e].position.y += middleDiff[1]
            
class TransitionLAY( layout.GraphEdgeLayouter ):
    def moveLabels(self, sizeHinter, startDiff, endDiff):

        edge=self.graphElement
        assert len(edge.contained)==2

        middleDiff=((startDiff[0]+endDiff[0])/2,(startDiff[1]+endDiff[1])/2)
        
        edge.contained[0].position.x += middleDiff[0]
        edge.contained[0].position.y += middleDiff[1]
            
        edge.contained[1].position.x += middleDiff[0]
        edge.contained[1].position.y += middleDiff[1]

class GeneralizationLAY( layout.GraphEdgeLayouter ):
    pass

class LinkLAY( layout.GraphEdgeLayouter ):

    def moveLabels(self, sizeHinter, startDiff, endDiff):
        edge=self.graphElement
        middleDiff=((startDiff[0]+endDiff[0])/2,(startDiff[1]+endDiff[1])/2)        
        if not layout.floatEquals(startDiff, endDiff) or 1:
            for st in edge.contained:
                st.position.x += middleDiff[0]
                st.position.y += middleDiff[1]

class DependencyLAY( layout.GraphEdgeLayouter ):
    def moveLabels(self, sizeHinter, startDiff, endDiff):
        edge=self.graphElement
        # BUG Can we assert this really?
        #assert len(edge.contained)==1
        if len(edge.contained)==1:
            middleDiff=((startDiff[0]+endDiff[0])/2,(startDiff[1]+endDiff[1])/2)
            self.graphElement.contained[0].position.x += middleDiff[0]
            self.graphElement.contained[0].position.y += middleDiff[1]

class CommentLinkLAY( layout.GraphEdgeLayouter ):
    def moveLabels(self, sizeHinter, startDiff, endDiff):
        # No labels exist
        pass

