"""
Classes related (dependency, implementation) adapter connections.
"""

from gaphor.adapters.connectors import RelationshipConnect
from zope import interface, component
from gaphor import UML
from gaphor.diagram import items

class DependencyConnect(RelationshipConnect):
    """
    Connect two NamedItem elements using a Dependency
    """
    component.adapts(items.NamedItem, items.DependencyItem)

    def allow(self, handle, port):
        line = self.line
        element = self.element

        # Element should be a NamedElement
        if not element.subject or \
                not isinstance(element.subject, UML.NamedElement):
                    return False

        if handle is line.tail \
                and line.dependency_type is UML.Realization \
                and not isinstance(element.subject, UML.Component):
            return False

        return super(DependencyConnect, self).allow(handle, port)

    def connect_subject(self, handle):
        """
        TODO: cleck for existing relationships (use self.relation())
        """
        line = self.line

        if line.auto_dependency:
            canvas = line.canvas
            opposite = line.opposite(handle)

            if handle is line.head:
                client = self.get_connected(opposite).subject
                supplier = self.element.subject
            else:
                client = self.element.subject
                supplier = self.get_connected(opposite).subject
            line.dependency_type = UML.model.dependency_type(client, supplier)

        if line.dependency_type is UML.Realization:
            relation = self.relationship_or_new(line.dependency_type,
                                head=('realizingClassifier', None),
                                tail=('abstraction', 'realization'))
        else:
            relation = self.relationship_or_new(line.dependency_type,
                                ('supplier', 'supplierDependency'),
                                ('client', 'clientDependency'))
        line.subject = relation

component.provideAdapter(DependencyConnect)


class GeneralizationConnect(RelationshipConnect):
    """
    Connect Classifiers with a Generalization relationship.
    """
    # FixMe: Both ends of the generalization should be of the same  type?
    component.adapts(items.ClassifierItem, items.GeneralizationItem)

    def connect_subject(self, handle):
        log.debug('connect_subject: ' % handle)
        relation = self.relationship_or_new(UML.Generalization,
                    ('general', None),
                    ('specific', 'generalization'))
        log.debug('found: ' % relation)
        self.line.subject = relation

component.provideAdapter(GeneralizationConnect)



class AssociationConnect(RelationshipConnect):
    """
    Connect association to classifier.
    """
    component.adapts(items.ClassifierItem, items.AssociationItem)

    CAN_BE_UNARY = True    # allow one classifier to be connected by association

    def allow(self, handle, port):
        element = self.element

        # Element should be a Classifier
        if not isinstance(element.subject, UML.Classifier):
            return None

        return super(AssociationConnect, self).allow(handle, port)

    def connect_subject(self, handle):
        element = self.element
        line = self.line

        c1 = self.get_connected(line.head)
        c2 = self.get_connected(line.tail)
        if c1 and c2:
            head_type = c1.subject
            tail_type = c2.subject

            # First check if we do not already contain the right subject:
            if line.subject:
                end1 = line.subject.memberEnd[0]
                end2 = line.subject.memberEnd[1]
                if (end1.type is head_type and end2.type is tail_type) \
                   or (end2.type is head_type and end1.type is tail_type):
                    return
                    
            # Create new association
            relation = UML.model.create_association(self.element_factory, head_type, tail_type)
            relation.package = element.canvas.diagram.namespace

            line.head_end.subject = relation.memberEnd[0]
            line.tail_end.subject = relation.memberEnd[1]

            # Do subject itself last, so event handlers can trigger
            line.subject = relation

    def disconnect_subject(self, handle):
        """
        Disconnect model element.
        Disconnect property (memberEnd) too, in case of end of life for
        Extension
        """
        opposite = self.line.opposite(handle)
        c1 = self.get_connected(handle)
        c2 = self.get_connected(opposite)
        if c1 and c2:
            old = self.line.subject
            del self.line.subject
            del self.line.head_end.subject
            del self.line.tail_end.subject
            if old and len(old.presentation) == 0:
                for e in list(old.memberEnd):
                    e.unlink()
                old.unlink()


component.provideAdapter(AssociationConnect)



class ImplementationConnect(RelationshipConnect):
    """
    Connect Interface and a BehavioredClassifier using an Implementation.
    """
    component.adapts(items.NamedItem, items.ImplementationItem)

    def allow(self, handle, port):
        line = self.line
        element = self.element

        # Element at the head should be an Interface
        if handle is line.head and \
           not isinstance(element.subject, UML.Interface):
            return None

        # Element at the tail should be a BehavioredClassifier
        if handle is line.tail and \
           not isinstance(element.subject, UML.BehavioredClassifier):
            return None

        return super(ImplementationConnect, self).allow(handle, port)


    def connect_subject(self, handle):
        """
        Perform implementation relationship connection.
        """
        relation = self.relationship_or_new(UML.Implementation,
                    ('contract', None),
                    ('implementatingClassifier', 'implementation'))
        self.line.subject = relation


component.provideAdapter(ImplementationConnect)

# vim:sw=4:et:ai
