'''
Created on 02.02.2012

@author: kca
'''

from time import sleep
from abc import ABCMeta, abstractproperty, abstractmethod
from futile import Base
from futile.path import Path
from . import check_call, STDOUT

class DaemonController(Base):
	__metaclass__ = ABCMeta
	
	def __init__(self, sleep = 5, stop_sleep = 3, *args, **kw):
		super(DaemonController, self).__init__(*args, **kw)
		self.__sleep = int(sleep)
		self.__stop_sleep = int(stop_sleep)

	@abstractproperty
	def is_running(self):
		raise NotImplementedError()

	def start(self):
		self._start()
		sleep(self.__sleep)

	@abstractmethod
	def _start(self):
		raise NotImplementedError()

	def stop(self):
		self._stop()
		sleep(self.__stop_sleep)

	@abstractmethod
	def _stop(self):
		raise NotImplementedError()
	
	def __enter__(self):
		self.start()
		return self
	
	def __exit__(self, exc_type, exc_val, exc_tb):
		self.stop()

class DummyController(DaemonController):
	def __init__(self, sleep = 0, stop_sleep = 0, *args, **kw):
		super(DummyController).__init__(sleep = sleep, stop_sleep = stop_sleep, *args, **kw)
	
	def _start(self):
		pass
	_stop = _start

	@property
	def is_running(self):
		return False

import os
import errno

class CheckPIDFileController(DaemonController):
	def __init__(self, pidfile, *args, **kw):
		super(CheckPIDFileController, self).__init__(*args, **kw)
		self.__pidfile = Path(pidfile)

	@property
	def pidfile(self):
		return self.__pidfile

	@property
	def is_running(self):
		if not self.pidfile.exists():
			return False

		if not self.pidfile.isfile():
			raise Exception("pidfile '%s' is not a file" % (self.pidfile, ))

		try:
			pid = int(self.__pidfile.open().readline(16))
		except:
			self.logger.exception("Error reading pidfile %s" % (self.pidfile))
			raise

		try:
			os.kill(pid, 0)
			return True
		except OSError as e:
			if e.errno == errno.ESRCH:
				return False
			raise

class StartStopDaemonController(CheckPIDFileController):
	def __init__(self, executable, fork = False, workingdir = None, pidfile = None, makepidfile = False, daemonargs = None, ssd = "/sbin/start-stop-daemon", ldpath = None, outfile = "/dev/null", *args, **kw):
		if not pidfile:
			pidfile = "/tmp/" + executable.replace("/", "_") + ".pid"
		super(StartStopDaemonController, self).__init__(pidfile = pidfile, *args, **kw)

		self.__executable = unicode(executable)
		self.__workingdir = workingdir and unicode(workingdir) or None

		if ldpath is not None:
			if not isinstance(ldpath, (list, set, tuple, frozenset)):
				ldpath = [ ldpath ]
			ldpath = tuple(set(ldpath))
		self.__ldpath = ldpath

		self.__makepidfile = makepidfile
		self.__daemonargs = daemonargs
		self.__fork = fork
		self.__ssd = ssd
		self.__outfile = outfile

	def get_daemonargs(self):
		return self.__daemonargs
	def set_daemonargs(self, da):
		self.__daemonargs = da
	daemonargs = property(get_daemonargs, set_daemonargs)

	def __make_cmd(self, cmd, test):
		cmd = [ self.__ssd, cmd, '-x', self.__executable, '-p', self.pidfile, '-o' ]

		if self.__workingdir:
			cmd += [ '-d', self.__workingdir ]

		if test:
			cmd.append('-t')
	
		env = None
		if self.__ldpath:
			env = dict(LD_LIBRARY_PATH = ':'.join(self.__ldpath))

		return cmd, env

	def __check_cmd(self, cmd, env):
		self.logger.debug("ssd env: " + str(env))

		outfile = self.__outfile
		if outfile:
			outfile = Path(outfile).open("a")

		try:
			check_call(cmd, stdout = outfile, stderr = STDOUT, close_fds = True, cwd = self.__workingdir, env = env)
		finally:
			if outfile is not None:
				outfile.close()

	def _start(self):
		cmd, env = self.__make_cmd("-S", False)
		if self.__makepidfile:
			cmd.append('-m')

		if self.__fork:
			cmd.append('-b')

		if self.__daemonargs:
			cmd += [ '--' ] + list(self.__daemonargs)

		self.__check_cmd(cmd, env)

	def _stop(self):
		cmd, env = self.__make_cmd("-K", False)
		self.__check_cmd(cmd, env)

