# This code is part of Grandalf
# Copyright (C) 2008-2015 Axel Tillequin (bdcht3@gmail.com) and others
# published under GPLv2 license or EPLv1 license
# Contributor(s): Axel Tillequin

from collections import OrderedDict

#------------------------------------------------------------------------------
# Poset class implements a set but allows to interate over the elements in a
# deterministic way and to get specific objects in the set.
# Membership operator defaults to comparing __hash__  of objects but Poset
# allows to check for __cmp__/__eq__ membership by using contains__cmp__(obj)
class  Poset(object):

    def __init__(self,L):
        self.o = OrderedDict()
        for obj in L:
            self.add(obj)

    def __repr__(self):
        return 'Poset(%r)' % (self.o,)

    def __str__(self):
        f='%%%dd'%len(str(len(self.o)))
        s=[]
        for i,x in enumerate(self.o.values()):
            s.append(f%i+'.| %s'%repr(x))
        return '\n'.join(s)

    def add(self,obj):
        if obj in self:
            return self.get(obj)
        else:
            self.o[obj] = obj
            return obj

    def remove(self,obj):
        if obj in self:
            obj = self.get(obj)
            del self.o[obj]
            return obj
        return None

    def index(self,obj):
        return list(self.o.values()).index(obj)

    def get(self,obj):
        return self.o.get(obj,None)

    def __getitem__(self,i):
        return list(self.o.values())[i]

    def __len__(self):
        return len(self.o)

    def __iter__(self):
        for obj in iter(self.o.values()):
            yield obj

    def __cmp__(self,other):
        s1 = set(other.o.values())
        s2 = set(self.o.values())
        return cmp(s1,s2)

    def __eq__(self,other):
        s1 = set(other.o.values())
        s2 = set(self.o.values())
        return s1==s2

    def __ne__(self,other):
        s1 = set(other.o.values())
        s2 = set(self.o.values())
        return s1!=s2

    def copy(self):
        return Poset(self.o.values())

    __copy__ = copy
    def deepcopy(self):
        from copy import deepcopy
        L = deepcopy(self.o.values())
        return Poset(L)

    def __or__(self,other):
        return self.union(other)

    def union(self,other):
        p = Poset([])
        p.o.update(self.o)
        p.o.update(other.o)
        return p

    def update(self,other):
        self.o.update(other.o)

    def __and__(self,other):
        s1 = set(self.o.values())
        s2 = set(other.o.values())
        return Poset(s1.intersection(s2))

    def intersection(self,*args):
        p = self
        for other in args:
            p = p & other
        return p

    def __xor__(self,other):
        s1 = set(self.o.values())
        s2 = set(other.o.values())
        return Poset(s1.symmetric_difference(s2))

    def symmetric_difference(self,*args):
        p = self
        for other in args:
            p = p ^ other
        return p

    def __sub__(self,other):
        s1 = set(self.o.values())
        s2 = set(other.o.values())
        return Poset(s1.difference(s2))

    def difference(self,*args):
        p = self
        for other in args:
            p = p - other
        return p

    def __contains__(self,obj):
        return (obj in self.o)

    def contains__cmp__(self,obj):
        return (obj in self.o.values())

    def issubset(self,other):
        s1 = set(self.o.values())
        s2 = set(other.o.values())
        return s1.issubset(s2)

    def issuperset(self,other):
        s1 = set(self.o.values())
        s2 = set(other.o.values())
        return s1.issuperset(s2)

    __le__ = issubset
    __ge__ = issuperset

    def __lt__(self,other):
        return (self<=other and len(self)!=len(other))

    def __gt__(self,other):
        return (self>=other and len(self)!=len(other))
