# coding=utf8
from __future__ import unicode_literals, absolute_import, print_function


class Accessor(object):

    pass


class IndexAccessor(Accessor):

    def __init__(self, index):
        assert isinstance(index, int)
        assert 0 <= index
        self.index = index

    def __str__(self):
        return '[{}]'.format(self.index)


class AttributeAccessor(Accessor):

    def __init__(self, attr):
        assert isinstance(attr, basestring)
        self.attr = attr

    def __str__(self):
        return '.{}'.format(self.attr)


class UnionAccessor(Accessor):

    def __init__(self, index):
        assert isinstance(index, int)
        self.index = index

    def __str__(self):
        return '#union[{}]'.format(self.index)


class AccessorChain(object):

    def __init__(self, _chain=None, prefix=None):
        self._prefix = prefix
        self._chain = _chain or list()
        for ac in self._chain:
            assert isinstance(ac, Accessor)

    def __mod__(self, other):
        assert isinstance(other, Accessor)
        chain = list(self._chain)
        chain.append(other)
        return AccessorChain(chain, self._prefix)

    def __str__(self):
        return (self._prefix or '') + ''.join((str(x) for x in self._chain))


class Conflict(object):

    def __init__(self, chain, expected, got, message=None):
        assert isinstance(chain, AccessorChain)
        self.chain = chain
        self.expected = expected
        self.got = got
        self.message = message

    @property
    def exception(self):
        return ConflictException(self)

    def __str__(self):
        return '@ {}, expected {}, got {}'.format(str(self.chain), str(self.expected), str(self.got))


class UnionConflict(Conflict):

    def __init__(self, chain, expected, got, choices):
        for c in choices:
            assert isinstance(c, Conflict)
        super(UnionConflict, self).__init__(chain=chain, expected=expected, got=got)
        self.choices = choices

    def __str__(self):
        return '{}\n[[[\n{}\n]]]'.format(str(self.chain), '\n'.join([str(c) for c in self.choices]))


class ConflictException(Exception):

    def __init__(self, conflict):
        self.conflict = conflict
        msg = ''
        if conflict.message:
            msg += conflict.message + '{} '
        msg += str(conflict)
        super(ConflictException, self).__init__(msg)
