#
# Thanks to Steve Freitas (sflist at ihonk.com)
# for providing this workaround in source format, ready to use.
# For more information on the need to pass DETACHED_PROCESS, see:
# http://twistedmatrix.com/pipermail/twisted-python/2007-February/014757.html
#
import os

from twisted.internet import defer, error, protocol
from twisted.python import log, runtime


class HiddenSpawnedProcess(protocol.ProcessProtocol):
	def __init__(self):
		from twisted.internet import reactor
		self.win32_detached = True
		try:
			if runtime.platform.isWindows() and not hasattr(reactor, '_upgraded_to_win32flags'):
				from win32SpawnProcess import upgradeSpawnProcess
				upgradeSpawnProcess()
				reactor._upgraded_to_win32flags = True
				log.msg('Upgraded reactor.spawnProcess()')
		except Exception, e:
			log.err(e)

	def startProcess(self, args, env=os.environ, cb_stdout=None, cb_stderr=None, path=None, usePTY=None):
		path2exec = os.path.dirname(args[0])
		executable = args[0]
		if path is None:
			path = path2exec # RFB wouldn't run on OS X without this
		self.cb_stdout = cb_stdout
		self.cb_stderr = cb_stderr
		self.defer_end = None
		self.processExitedAlready = False

		from twisted.internet import reactor
		# Note: spawnProcess calls self.connectionMade synchronously (read: immediately) if successful
		if runtime.platform.isWindows():
			import win32con			#@UnresolvedImport
			flags = 0
			if self.win32_detached:
				flags = win32con.DETACHED_PROCESS
			return reactor.spawnProcess(self, os.path.join(path2exec, executable), path=path,		#@UndefinedVariable
								 args=args, env=env, win32flags=flags)
		else:
			log.msg('Starting %s with arguments: %s' % (os.path.join(path2exec, executable), args))
			return reactor.spawnProcess(self, os.path.join(path2exec, executable), path=path,		#@UndefinedVariable
								 args=args, env=env, usePTY=usePTY)

	def getProcessEndedDeferred(self):
		if self.defer_end is None:
			self.defer_end = defer.Deferred()
		return self.defer_end

	def write(self, data):
		self.transport.write(data)

	def kill(self):
		if not self.processExitedAlready:
			try:
				self.transport.signalProcess('KILL')
			except AttributeError , error.ProcessExitedAlready:
				pass
			self.processExitedAlready = True

	def connectionMade(self):
		pass

	def outReceived(self, data):
		if self.cb_stdout:
			self.cb_stdout(data)

	def errReceived(self, data):
		if self.cb_stderr:
			self.cb_stderr(data)

	def processEnded(self, reason):
		self.processExitedAlready = True
		if self.defer_end:
			self.defer_end.callback(reason)
