from parallels.core import messages
from parallels.core.actions.base.base_action \
    import BaseAction
from parallels.core.actions.utils.logging_properties import LoggingProperties


class CompoundAction(BaseAction):
    """Action in workflow which consists of other actions"""
    def __init__(self, description=None, logging_properties=None, run_by_subscription=True):
        self._actions = []
        self._description = description
        if logging_properties is not None:
            self._logging_properties = logging_properties
        else:
            self._logging_properties = LoggingProperties()
        self._run_by_subscription = run_by_subscription

    def get_description(self):
        return self._description

    def get_logging_properties(self):
        return self._logging_properties

    @property
    def run_by_subscription(self):
        return self._run_by_subscription

    def filter_action(self, global_context):
        """Check whether we should run this action or not. By default True - action should be executed.

        Arguments:
        - global_context - registry with different objects that reused among different actions

        :type global_context: parallels.core.global_context.GlobalMigrationContext
        :rtype: bool
        """
        return True

    def insert_action(self, name, action, after=None, before=None):
        self.insert_actions(
            [(name, action,)], after=after, before=before
        )

    def remove_action(self, name):
        self.remove_actions([name])

    def replace(self, name, action):
        new_actions = []
        for existing_name, existing_action in self._actions:
            if name == existing_name:
                new_actions.append((name, action))
            else:
                new_actions.append((existing_name, existing_action))

        self._actions = new_actions

    def insert_first_action(self, name, action):
        """
        Insert action to the first position

        :param name: action id(name),
        :param action: action object
        :return: None
        """
        self._actions.insert(0, (name, action))

    def insert_actions(self, actions, after=None, before=None):
        """Insert multiple actions into workflow

        :param actions: list of actions - tuples (action id, action object)
        :param after: string id of action, all actions will be put after that action
        :param before: string id of action, all actions will be put before that action
        :return: None

        'after' and 'before' parameters are mutually exclusive
        """
        assert after is None or before is None
        i = 0
        name = None
        for i, (name, _) in enumerate(self._actions):
            if name == after:
                break
            if name == before:
                i -= 1
                break

        if after is not None and name != after:
            raise Exception(messages.FAILED_FIND_ACTION_S_AFTER_WHICH % after)
        if before is not None and name != before:
            raise Exception(messages.FAILED_FIND_ACTION_S_BEFORE_WHICH % before)

        for j, action in enumerate(actions):
            self._actions.insert(i + j + 1, action)

    def remove_actions(self, names):
        new_actions = []
        for name, action in self._actions:
            if name not in names:
                new_actions.append((name, action))
        self._actions = new_actions

    def get_action(self, name):
        return dict(self._actions)[name]

    def get_all_actions(self):
        return self._actions

    def get_path(self, path):
        current = self
        for item in path.split('/'):
            current = current.get_action(item)
        return current

    def clone_without_children(self):
        return CompoundAction(
            self.get_description(),
            self.get_logging_properties(),
            self.run_by_subscription
        )