from django.core.urlresolvers import reverse


class Node(object):
    def __init__(self, name=None, icon=None, link=None):
        self.parent = None
        self.name = name
        self.icon = icon
        self.link = link
        self._children = []
        self._children_dict = {}

    @property
    def namespace(self):
        try:
            return self._resolved_namespace
        except AttributeError:
            bits = self.link.split(':')
            self._resolved_namespace = ':'.join(bits[:-1])

        return self._resolved_namespace

    def children(self):
        return self._children

    def children_as_dicts(self):
        childrens = []
        for children in self._children:
            childrens.append(
                {
                    'name': children.name,
                    'icon': children.icon,
                    'link': reverse(children.link),
                    'namespace': children.namespace,
                })
        return childrens

    def add_node(self, node, after=None, before=None):
        if after:
            return self.add_node_after(node, after)
        elif before:
            return self.add_node_before(node, before)
        else:
            node.parent = self
            self._children.append(node)
            self._children_dict[node.link] = node
            return True

    def add_node_after(self, node, after):
        success = False
        new_children_list = []

        for children in self._children:
            new_children_list.append(children)
            if children.link == after:
                new_children_list.append(node)
                success = True

        if success:
            node.parent = self
            self._children_dict[node.link] = node
            self._children = new_children_list
        return success

    def add_node_before(self, node, before):
        success = False
        new_children_list = []

        for children in self._children:
            if children.link == before:
                new_children_list.append(node)
                success = True
            new_children_list.append(children)

        if success:
            node.parent = self
            self._children_dict[node.link] = node
            self._children = new_children_list
        return success

    def child(self, namespace):
        try:
            return self._children_dict[namespace]
        except KeyError:
            raise ValueError(
                "Node %s is not a child of node %s" % (namespace, self.name))

    def is_root(self):
        return False


class AdminHierarchyBuilder(object):
    def __init__(self):
        self.nodes_record = []
        self.nodes_dict = {}

    def build_nodes_dict(self):
        nodes_dict = {'misago:admin': Node(link='misago:admin:index')}

        iterations = 0
        while self.nodes_record:
            iterations += 1
            if iterations > 512:
                message = ("Misago Admin hierarchy is invalid or too complex "
                           "to resolve. Nodes left: %s")
                raise ValueError(message % self.nodes_record)

            for index, node in enumerate(self.nodes_record):
                if node['parent'] in nodes_dict:
                    node_obj = Node(name=node['name'],
                                    icon=node['icon'],
                                    link=node['link'])

                    parent = nodes_dict[node['parent']]
                    if node['after']:
                        node_added = parent.add_node(node_obj,
                                                     after=node['after'])
                    elif node['before']:
                        node_added = parent.add_node(node_obj,
                                                     before=node['before'])
                    else:
                        node_added = parent.add_node(node_obj)

                    if node_added:
                        namespace = node.get('namespace') or node_obj.namespace

                        if namespace not in nodes_dict:
                            nodes_dict[namespace] = node_obj

                        del self.nodes_record[index]
                        break

        return nodes_dict

    def add_node(self, name=None, icon=None, parent='misago:admin', after=None,
                 before=None, namespace=None, link=None):
        if self.nodes_dict:
            raise RuntimeError("Misago admin site has already been "
                               "initialized. You can't add new nodes to it.")

        if after and before:
            raise ValueError("after and before arguments are exclusive")

        self.nodes_record.append(
            {
                'name': name,
                'icon': icon,
                'parent': parent,
                'namespace': namespace,
                'after': after,
                'before': before,
                'link': link,
            })

    def visible_branches(self, request):
        if not self.nodes_dict:
            self.nodes_dict = self.build_nodes_dict()

        branches = []

        if request.resolver_match.namespace in self.nodes_dict:
            node = self.nodes_dict[request.resolver_match.namespace]
            while node:
                children = node.children_as_dicts()
                if children:
                    branches.append(children)
                node = node.parent

        namespace = request.resolver_match.namespaces

        branches.reverse()
        for depth, branch in enumerate(branches):
            depth_namespace = namespace[2:3 + depth]
            for node in branch:
                node_namespace = node['namespace'].split(':')[2:3 + depth]
                node['is_active'] = depth_namespace == node_namespace

        return branches


site = AdminHierarchyBuilder()