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

from ..parser.docstring import DocString
from ..typedef.nodes import Node
from ..typedef.compose import compose
from ..typing.basic import Type
from ..accessor import ConflictException, AccessorChain


class DocumentLoader(object):

    def __init__(self, configuration_map):
        self._conf_map = configuration_map

    def parse_docstring(self, docstring):
        ds_obj = DocString.parse(docstring)
        if not ds_obj.straight_text:
            return None
        conf = self.get_configuration(ds_obj.conf_args)
        func_doc = conf.parse(ds_obj)
        assert isinstance(func_doc, FunctionDocument)
        return func_doc

    def get_configuration(self, conf_args):
        assert len(conf_args) > 0, conf_args
        try:
            return self._conf_map[conf_args[0]]()
        except KeyError:
            raise Exception("Unknown Configuration: {}".format(repr(conf_args)))


class FunctionDocument(object):

    def __init__(self, params, result, description):
        assert isinstance(params, TypeDocument)
        assert isinstance(result, TypeDocument)
        assert isinstance(description, basestring)
        self._params = params
        self._result = result
        self._desc = description

    @property
    def params(self):
        return self._params

    @property
    def result(self):
        return self._result

    @property
    def description(self):
        return self._desc


class TypeDocument(object):

    def __init__(self, name, conf_args, ast, typing, description):
        assert isinstance(conf_args, list)
        for w in conf_args:
            assert isinstance(w, basestring)
        assert isinstance(ast, Node)
        assert isinstance(typing, Type)
        assert isinstance(description, basestring)

        self._name = name
        self._conf_args = conf_args
        self._ast = ast
        self._typing = typing
        self._desc = description

    @property
    def name(self):
        return self._name

    @property
    def conf_args(self):
        return self._conf_args

    @property
    def ast(self):
        return self._ast

    @property
    def ast_composed(self):
        return compose(self.ast)

    @property
    def typing(self):
        return self._typing
    
    @property
    def description(self):
        return self._desc

    def unify(self, item):
        chain = AccessorChain(prefix=self.name)
        try:
            self.typing.unify(chain, item)
        except ConflictException as e:
            return e.conflict
        else:
            return None

