from parallels.core import messages
from parallels.core.actions.base.action_pointer import ActionPointer


class BaseWorkflow(object):
    def __init__(self):
        self._entry_points = dict()
        self._shared_actions = dict()

    def add_entry_point(self, name, entry_point):
        if name in self._entry_points:
            raise Exception(messages.ENTRY_POINT_S_ALREADY_EXISTS % name)
        self._entry_points[name] = entry_point

    def replace_entry_point(self, name, entry_point):
        if name not in self._entry_points:
            raise Exception(
                messages.CAN_NOT_REPLACE_ENTRY_POINT_S % name
            )
        self._entry_points[name] = entry_point

    def get_entry_point(self, name):
        if name not in self._entry_points:
            raise Exception(
                messages.MIGRATOR_INTERNAL_ERROR_ENTRY_POINT_ID % name
            )
        return self._entry_points[name]

    def get_entry_points(self):
        return self._entry_points

    def get_path(self, path):
        """
        :rtype: parallels.core.actions.base.entry_point_action.EntryPointAction
        """
        if '/' in path:
            entry_point, action_path = path.split('/', 1)
            return self.get_entry_point(entry_point).get_path(action_path)
        else:
            return self.get_entry_point(path)

    def get_shared_action(self, action_id):
        """Get action shared between other actions from registry

        To use such action in other actions, you could use the code like
        the following:
        1) Add and configure action to the registry:
        self.add_common_action('common-action-1', CompoundAction())
        # configure action there
        2) Add it to workflow:
            self
                .get_path('entry-point/action1/subaction1')
                .get_common_action('common-action-1')
            You could add it multiple paths in workflow
        """
        return self.get_shared_action_pointer(action_id).resolve()

    def get_shared_action_pointer(self, action_id):
        """Get pointer to action shared between other actions from registry

        Pointer allows to link to shared action dynamically,
        so even if you replace an action, all usages of that pointer
        are up-to-date.

        To use such action in other actions, you could use the code like
        the following:
        1) Add and configure action to the registry:
        self.add_common_action('common-action-1', CompoundAction())
        # configure action there
        2) Add it to workflow:
            self
                .get_path('entry-point/action1/subaction1')
                .get_common_action('common-action-1')
            You could add it multiple paths in workflow
        """
        if action_id not in self._shared_actions:
            raise Exception(messages.MIGRATOR_INTERNAL_ERROR_SHARED_ACTION_ID % action_id)
        return self._shared_actions[action_id]

    def add_shared_action(self, action_id, action):
        """Add action that could be shared between other actions to registry

        To use such action in other actions, you could use the code like
        the following:
        1) Add and configure action to the registry:
        self.add_common_action('common-action-1', CompoundAction())
        # configure action there
        2) Add it to workflow:
            self
                .get_path('entry-point/action1/subaction1')
                .get_common_action('common-action-1')
            You could add it multiple paths in workflow
        """
        self._shared_actions[action_id] = ActionPointer(action)

    def replace_shared_action(self, action_id, action):
        """Replace existing shared action with another one

        See "add_shared_action" and "get_shared_action" functions for more details on
        shared actions.
        """
        if action_id not in self._shared_actions:
            raise Exception(messages.CAN_NOT_REPLACE_SHARED_ACTION_SHARED % action_id)
        self._shared_actions[action_id].set(action)

    def insert_first_shared_action_to_all_entries(self, action_id, action):
        """
        Method adds specified action to all entries at the first position in their actions list

        See "add_shared_action" and "get_shared_action" functions for more details on
        shared actions.

        :param action_id: string
        :param action: BaseAction instance of action
        :return: None
        """
        for entry_point_action in self._entry_points.values():
            entry_point_action.insert_first_action(action_id, action)