# coding=utf-8
import _jb_utils


class ParallelTreeManager(object):
    """
    Manages output tree by building it from flat test names.
    """

    def __init__(self, offset):
        super(ParallelTreeManager, self).__init__()
        self._max_node_id = offset
        self.offset = offset
        self._branches = dict()  # key is test name as tuple, value is tuple of test_id, parent_id

    def _next_node_id(self):
        self._max_node_id += 1
        return self._max_node_id

    def level_opened(self, test_as_list, func_to_open):
        """
        To be called on test start.

        :param test_as_list: test name splitted as list
        :param func_to_open: func to be called if test can open new level
        :return: None if new level opened, or tuple of command client should execute and try opening level again
         Command is "open" (open provided level) or "close" (close it). Second item is test name as list
        """

        if tuple(test_as_list) in self._branches:
            # We have parent, ok
            func_to_open()
            return None
        elif len(test_as_list) == 1:
            self._branches[tuple(test_as_list)] = (self._next_node_id(), self.offset)
            func_to_open()
            return None

        commands = []

        parent_id = self.offset
        for i in range(len(test_as_list)):
            tmp_parent_as_list = test_as_list[0:i + 1]
            try:
                parent_id, _ = self._branches[tuple(tmp_parent_as_list)]
            except KeyError:
                node_id = self._next_node_id()
                self._branches[tuple(tmp_parent_as_list)] = (node_id, parent_id)
                parent_id = node_id
                if tmp_parent_as_list != test_as_list: # Different test opened
                    commands.append(("open", tmp_parent_as_list))
        if commands:
            return commands
        else:
            func_to_open()

    def level_closed(self, test_as_list, func_to_close):
        """
        To be called on test end or failure.

        See level_opened doc.
        """
        func_to_close()

    # Part of contract
    def get_node_ids(self, test_name):
        """

        :return: (current_node_id, parent_node_id) or None, None if message must be ignored
        """
        try:
            return self._branches[tuple(_jb_utils.test_to_list(test_name))]
        except KeyError:
            return None, None
