from dp2 import util, predicates

class RegisteredCorporalClass(type):
    def __init__(self, name, bases, namespace):
        type.__init__(self, name, bases, namespace)

        if not namespace.has_key('__init__'):
            def __init__(self, *args, **kiargs):
                for base in bases:
                    base.__init__(self, *args, **kiargs)
            self.__init__ = __init__

        name = util.normalise_symbol(name)
        setattr(predicates,
                'is_' + name.replace('-', '_'),
                lambda i: isinstance(i, self))

__metaclass__ = RegisteredCorporalClass

class Action:
    def __init__(self, actor, *iargs, **kiargs):
        self.actor = actor

class Affection(Action):
    def __init__(self, target, *iargs, **kiargs):
        Action.__init__(self, *iargs, **kiargs)
        self.target = target

class Presentation(Affection):
    def __init__(self, object, *iargs, **kiargs):
        Affection.__init__(self, *iargs, **kiargs)
        self.object = object

class Listen(Affection):
    pass

class Close(Affection):
    pass

class Request(Affection):
    def __init__(self, item, *iargs, **kiargs):
        self.item = item
        Affection.__init__(self, *iargs, **kiargs)

class Alert(Affection):
    def __init__(self, condition, *iargs, **ikargs):
        self.condition = condition
        Affection.__init__(self, *iargs, **ikargs)

class Cons:
    """cons*
    """
    def __init__(self, car, cdr, *rest):
        self.car = car
        if rest:
            self.cdr = Cons(cdr, *rest)
        else:
            self.cdr = cdr
    def __getattr__(self, name):
        if len(name) > 3 \
               and name[0] == 'c' \
               and name[-1] == 'r':
            name = list(name)
            name.reverse()
            for op in name[1:-1]:
                if op == 'a':
                    self = self.car
                elif op == 'd':
                    self = self.cdr
                else:
                    raise AttributeError
            return self
        else:
            raise AttributeError
    def __setattr__(self, name, value):
        if len(name) > 3 \
               and name[0] == 'c' \
               and name[-1] == 'r':
            setattr(getattr(self, 'c'+name[2:]), 'c'+name[1]+'r', value)
        else:
            self.__dict__[name] = value
    def __getitem__(self, item):
        if isinstance(item, int):
            if item > 0:
                if isinstance(self.cdr, Cons):
                    return self.cdr[item-1]
                else:
                    raise IndexError
            elif item < 0:
                stack = None
                while isinstance(self, Cons):
                    stack = Cons(self, stack)
                    self = self.cdr
                return stack[-item-1].car
            else:
                return self.car
        else:
            raise TypeError
    def __setitem__(self, item, value):
        if isinstance(item, int):
            if item > 0:
                if isinstance(self.cdr, Cons):
                    self.cdr[item-1] = value
                else:
                    raise IndexError
            elif item < 0:
                stack = None
                while isinstance(self, Cons):
                    stack = Cons(self, stack)
                    self = self.cdr
                stack[-item-1].car = value
            else:
                self.car = value
    def __repr__(self):
        s = '(' + repr(self.car)
        while isinstance(self.cdr, Cons):
            self = self.cdr
            s += ' ' + repr(self.car)
        else:
            if self.cdr is not None:
                s += ' . ' + repr(self.cdr)
        return s + ')'
    def __cmp__(self, other):
        if isinstance(other, Cons):
            result = cmp(self.car, other.car)
            if result:
                return result
            else:
                return cmp(self.cdr, other.cdr)
        else:
            return cmp(type(self).__name__, type(other).__name__)
    def __rcmp__(self, other):
        return -cmp(other, self)
    def __len__(self):
        length = 1
        if isinstance(self.cdr, Cons):
            length += len(self.cdr)
        return length
    def __add__(self, other):
        if isinstance(other, Cons) or other is None:
            last = self = copy(self)
            while isinstance(last.cdr, Cons):
                last.cdr = copy(last.cdr)
                last = last.cdr
            last.cdr = other
            return self
        else:
            raise TypeError
    def __radd__(*args):
        return Cons.__add__(*args)
    def __mul__(self, other):
        if isinstance(other, int):
            total = None
            if other > 0:
                for x in range(other):
                    total = self + total
            return total
        else:
            raise TypeError
    def __rmul__(*args):
        return Cons.__mul__(*args)
    def index(self, item):
        index = 0
        while isinstance(self, Cons):
            if self.car == item:
                return index
            index += 1
            self = self.cdr
        else:
            raise ValueError
    def extend(self, sequence):
        while isinstance(self.cdr, Cons):
            self = self.cdr
        if isinstane(sequence, Cons):
            self.cdr = sequence
        else:
            self.cdr = apply(Cons, list(sequence) + [None])
    def append(self, item):
        while isinstance(self.cdr, Cons):
            self = self.cdr
        self.cdr = Cons(item, None)
    def index(self, value):
        if self.car == value:
            return 0
        elif isinstance(self.cdr, Cons):
            return self.cdr.index(value) + 1
    def count(self, value):
        if self.car == value:
            found = 1
        else:
            found = 0
        if isinstance(self.cdr, Cons):
            return found + self.cdr.count(value)
        else:
            return found
    def insert(self, index, value): # XXX excessively naive:
        if index == 0:
            self.__dict__ = Cons(value, copy(self))
        else:
            attr = 'c' + ('d' * index) + 'r'
            setattr(self, attr, Cons(value, getattr(self, attr)))

class Path(Cons):
    def __init__(self, head, tail, *rest):
        self.head = head
        if rest:
            self.tail = Path(tail, *rest)
        else:
            self.tail = tail
    def __repr__(self):
        s = '(path: ' + repr(self.head)
        while isinstance(self.tail, Path):
            self = self.tail
            s += ' -> ' + repr(self.head)
        s += ' -> ' + repr(self.tail) + ')'
        return s
    def __hash__(self):
        return hash((self.head, self.tail))
    def __cmp__(self, other):
        return cmp((self.head, self.tail), other)
    def __getattr__(self, name):
        if name == 'car': return self.head
        elif name == 'cdr': return self.tail
        else: return Cons.__getattr__(self, name)
    def __setattr__(self, name, value):
        if name == 'car':
            self.head = value
        elif name == 'cdr':
            self.tail = value
        else:
            return Cons.__setattr__(self, name, value)
