from contextlib import contextmanager

from parallels.core.registry import Registry
from parallels.core.utils.deamon import demonize
from parallels.core.migrator_config import MultithreadingParams, MultithreadingStatus
from parallels.core.utils.steps_profiler import get_default_steps_profiler


class Runner(object):
	def __init__(self, multithreading=None):
		"""Class constructor
		:type multithreading: parallels.core.migrator_config.MultithreadingParams | None
		"""
		self._context = Registry.get_instance().get_context()
		if multithreading is None:
			self._multithreading = MultithreadingParams(status=MultithreadingStatus.DISABLED, num_workers=None)
		else:
			self._multithreading = multithreading

	def run_entry_point(self, entry_point_name):
		"""Run specified entry point and execute shutdown actions
		:type entry_point_name: str
		:rtype: None
		"""
		registry = Registry.get_instance()
		entry_point = registry.get_workflow().get_entry_point(entry_point_name)

		if self._context.options.async:
			demonize()

		registry.get_log().configure(self._context.options)

		with self._context.progress.report(entry_point_name):
			try:
				self.run(entry_point)
			finally:
				self._shutdown(entry_point)

	def run(self, action, action_id=None):
		raise NotImplementedError()

	def _shutdown(self, entry_point):
		raise NotImplementedError()

	@staticmethod
	@contextmanager
	def _profile_action(action_id, action, profiling_enabled=True):
		"""Measure time of specified action

		This function considers that for all parent actions this function was already called, and we're
		already in that context.

		:type action: parallels.core.actions.base.base_action.BaseAction
		:type action_id: basestring
		:type profiling_enabled: boolean
		"""
		if action.get_description() is not None and profiling_enabled:
			with get_default_steps_profiler().measure_time(action_id, action.get_description()):
				yield
		else:
			# skip profiling in one of following cases:
			# - compound actions that have no descriptions are profiled by all child actions
			# - other actions that have no descriptions are measured as "Other" action
			# - profiling was explicitly disabled
			yield
