# -*- coding: utf-8 -*-
#    Gedit External Tools plugin
#    Copyright (C) 2005-2006  Steve Frécinaux <steve@istique.net>
#
#    This program is free software; you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation; either version 2 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program; if not, write to the Free Software
#    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

__all__ = ('Capture', )

import os, sys, signal
import locale
import subprocess
import gobject

class Capture(gobject.GObject):
	CAPTURE_STDOUT = 0x01
	CAPTURE_STDERR = 0x02
	CAPTURE_BOTH   = 0x03

	__gsignals__ = {
		'stdout-line'  : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_STRING,)),
		'stderr-line'  : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_STRING,)),
		'begin-execute': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, tuple()),
		'end-execute'  : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_INT,))
	}

	def __init__(self, command, cwd = None, env = {}):
		gobject.GObject.__init__(self)
		self.pipe = None
		self.env = env
		self.cwd = cwd
		self.flags = self.CAPTURE_BOTH
		self.command = command
		self.input_text = None
	
	def set_env(self, **values):
		self.env.update(**values)
	
	def set_command(self, command):
		self.command = command
	
	def set_flags(self, flags):
		self.flags = flags
	
	def set_input(self, text):
		self.input_text = text
	
	def set_cwd(self, cwd):
		self.cwd = cwd
	
	def execute(self):
		if self.command is None:
			return
		
		# Initialize pipe
		popen_args = {
			'cwd'  : self.cwd,
			'shell': True,
			'env'  : self.env
		}
		
		if self.input_text is not None:
			popen_args['stdin'] = subprocess.PIPE
		if self.flags & self.CAPTURE_STDOUT:
			popen_args['stdout'] = subprocess.PIPE
		if self.flags & self.CAPTURE_STDERR:
			popen_args['stderr'] = subprocess.PIPE
		
		self.pipe = subprocess.Popen(self.command, **popen_args)
		
		# Signal
		self.emit('begin-execute')

		# IO
		if self.input_text is not None:
			self.pipe.stdin.write(self.input_text)
			self.pipe.stdin.close()
		if self.flags & self.CAPTURE_STDOUT:
			gobject.io_add_watch(self.pipe.stdout,
			                     gobject.IO_IN | gobject.IO_HUP,
			                     self.on_output)
		if self.flags & self.CAPTURE_STDERR:
			gobject.io_add_watch(self.pipe.stderr,
			                     gobject.IO_IN | gobject.IO_HUP,
			                     self.on_output)

		# Wait for the process to complete
		gobject.child_watch_add(self.pipe.pid, self.on_child_end)
		
	def on_output(self, source, condition):
		line = source.readline()

		if len(line) > 0:
			try:
				line = unicode(line, 'utf-8')
			except:
				line = unicode(line, 
				               locale.getdefaultlocale()[1],
				               'replace')

			if source == self.pipe.stdout:
				self.emit('stdout-line', line)
			else:
				self.emit('stderr-line', line)
			return True

		return False

	def stop(self, error_code = -1):
		if self.pipe is not None:
			os.kill(self.pipe.pid, signal.SIGTERM)
			self.pipe = None

	def on_child_end(self, pid, error_code):
		# In an idle, so it is emitted after all the std*-line signals
		# have been intercepted
		gobject.idle_add(self.emit, 'end-execute', error_code)
