# -*- 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

import ElementTree as et
import os
import gtk
from gtk import gdk
import gnomevfs
import gedit, gtksourceview
from outputpanel import OutputPanel
from capture import *

# ==== Data storage related functions ====

class ToolsTree:
	def __init__(self):
		self.xml_location = os.path.join(os.environ['HOME'], '.gnome2/gedit')

		if not os.path.isdir(self.xml_location):
			os.mkdir(self.xml_location)

		self.xml_location = os.path.join(self.xml_location, 'gedit-tools.xml')
		
		try:
			if not os.path.isfile(self.xml_location):
				self.tree = et.parse(self.get_stock_file())
			else:
				self.tree = et.parse(self.xml_location)
			self.root = self.tree.getroot()
		except:
			self.root = et.Element('tools')
			self.tree = et.ElementTree(self.root)
	
	def __iter__(self):
		return iter(self.root)
	
	def get_stock_file(self):
		dirs = os.getenv('XDG_DATA_DIR')
		if dirs:
			dirs = dirs.split(os.pathsep)
		else:
			dirs = ('/usr/local/share', '/usr/share')
		
		for d in dirs:
			f = os.path.join(d, 'gedit-2/plugins/externaltools/stock-tools.xml')
			if os.path.isfile(f):
				return f

		return None
	
	def save(self):
		self.tree.write(self.xml_location, 'UTF-8')
		
	def get_tool_from_accelerator(self, keyval, mod, ignore = None):
		if not self.root:
			return None
		
		for tool in self.root:
			if tool != ignore:
				skey, smod = gtk.accelerator_parse(default(tool.get('accelerator'), ''))
				if skey == keyval and mod == smod:
					return tool
		return None


def default(val, d):
	if val is not None:
		return val
	else:
		return d

# ==== UI related functions ====
APPLICABILITIES = ('all', 'titled', 'local', 'remote', 'untitled')

def insert_tools_menu(window, tools = None):
	window_data = dict()
	window.set_data("ToolsPluginCommandsData", window_data)

	if tools is None:
		tools = ToolsTree()

	manager = window.get_ui_manager()

	window_data['action_groups'] = dict()
	window_data['ui_id'] = manager.new_merge_id()

	i = 0;
	for tool in tools:
		menu_id = "ToolCommand%06d" % i
		
		ap = tool.get('applicability')
		if ap not in window_data['action_groups']:
			window_data['action_groups'][ap] = \
			    gtk.ActionGroup("GeditToolsPluginCommandsActions%s" % ap.capitalize())
			window_data['action_groups'][ap].set_translation_domain('gedit')
		
		window_data['action_groups'][ap].add_actions(
			[(menu_id, None, tool.get('label'),
			  tool.get('accelerator'), tool.get('description'),
			  capture_menu_action)],
			(window, tool))
		manager.add_ui(window_data['ui_id'],
		               '/MenuBar/ToolsMenu/ToolsOps_4',
		               menu_id, 
		               menu_id,
		               gtk.UI_MANAGER_MENUITEM,
		               False)
		i = i + 1

	for applic in APPLICABILITIES:
		if applic in window_data['action_groups']:
			manager.insert_action_group(window_data['action_groups'][applic], -1)

def remove_tools_menu(window):
	window_data = window.get_data("ToolsPluginCommandsData")

	manager = window.get_ui_manager()
	manager.remove_ui(window_data['ui_id'])
	for action_group in window_data['action_groups'].itervalues():
		manager.remove_action_group(action_group)
	window.set_data("ToolsPluginCommandsData", None)

def update_tools_menu(tools = None):
	for window in gedit.app_get_default().get_windows():
		remove_tools_menu(window)
		insert_tools_menu(window, tools)
		filter_tools_menu(window)
		window.get_ui_manager().ensure_update()

def filter_tools_menu(window):
	action_groups = window.get_data("ToolsPluginCommandsData")['action_groups']

	document = window.get_active_document()
	if document is not None:
		active_groups = ['all']
		uri = document.get_uri()
		if uri is not None:
			active_groups.append('titled')
			if gnomevfs.get_uri_scheme(uri) == 'file':
				active_groups.append('local')
			else:
				active_groups.append('remote')
		else:
			active_groups.append('untitled')
	else:
		active_groups = []

	for name, group in action_groups.iteritems():
		group.set_sensitive(name in active_groups)

# ==== Capture related functions ====
def capture_menu_action(action, window, node):

	# Get the panel
	panel = window.get_data("ToolsPluginWindowData")["output_buffer"]
	panel.show()
	panel.clear()

	# Configure capture environment
	try:
		cwd = os.getcwd()
	except OSError:
		cwd = os.getenv('HOME');

 	capture = Capture(node.text, cwd)
 	capture.env = os.environ.copy()
	capture.set_env(GEDIT_CWD = cwd)

	view = window.get_active_view()
	if view is not None:
		# Environment vars relative to current document
		document = view.get_buffer()
		uri = document.get_uri()
		if uri is not None:
			scheme = gnomevfs.get_uri_scheme(uri)
			name = os.path.basename(uri)
			capture.set_env(GEDIT_CURRENT_DOCUMENT_URI    = uri,
			                GEDIT_CURRENT_DOCUMENT_NAME   = name,
			                GEDIT_CURRENT_DOCUMENT_SCHEME = scheme)
			if scheme == 'file':
		 		path = gnomevfs.get_local_path_from_uri(uri)
				cwd = os.path.dirname(path)
				capture.set_cwd(cwd)
				capture.set_env(GEDIT_CURRENT_DOCUMENT_PATH = path,
				                GEDIT_CURRENT_DOCUMENT_DIR  = cwd)
		
		documents_uri = [doc.get_uri()
		                         for doc in window.get_documents()
		                         if doc.get_uri() is not None]
		documents_path = [gnomevfs.get_local_path_from_uri(uri)
		                         for uri in documents_uri
		                         if gnomevfs.get_uri_scheme(uri) == 'file']
		capture.set_env(GEDIT_DOCUMENTS_URI  = ' '.join(documents_uri),
		                GEDIT_DOCUMENTS_PATH = ' '.join(documents_path))

 	capture.set_flags(capture.CAPTURE_BOTH)

	# Assign the error output to the output panel
	panel.process = capture

	# Get input text
	input_type = default(node.get('input'), 'nothing')
	output_type = default(node.get('output'), 'output-panel')

	if input_type != 'nothing' and view is not None:
		if input_type == 'document':
			start, end = document.get_bounds()
		elif input_type == 'selection':
			try:
				start, end = document.get_selection_bounds()
			except ValueError:
				start, end = document.get_bounds()
				if output_type == 'replace-selection':
					document.select_range(start, end)
		elif input_type == 'line':
			start = document.get_iter_at_mark(document.get_insert())
			end = start.copy()
			if not start.starts_line():
				start.backward_line()
			if not end.ends_line():
				end.forward_to_line_end()
		elif input_type == 'word':
			start = document.get_iter_at_mark(document.get_insert())
			end = start.copy()
			if not start.inside_word():
				panel.write(_('You must be inside a word to run this command'),
				            panel.command_tag)
				return
			if not start.starts_word():
				start.backward_word_start()
			if not end.ends_word():
				end.forward_word_end()
			
		input_text = document.get_text(start, end)
		capture.set_input(input_text)
	
	# Assign the standard output to the chosen "file"
	if output_type == 'new-document':
		tab = window.create_tab(True)
		view = tab.get_view()
		document = tab.get_document()
		pos = document.get_start_iter()
		capture.connect('stdout-line', capture_stdout_line_document, document, pos)
		document.begin_user_action()
		view.set_editable(False)
		view.set_cursor_visible(False)
	elif output_type != 'output-panel' and view is not None:
		document.begin_user_action()
		view.set_editable(False)
		view.set_cursor_visible(False)
		
		if output_type == 'insert':
			pos = document.get_iter_at_mark(document.get_mark('insert'))
		elif output_type == 'replace-selection':
			document.delete_selection(False, False)
			pos = document.get_iter_at_mark(document.get_mark('insert'))
		elif output_type == 'replace-document':
			document.set_text('')
			pos = document.get_end_iter()
		else:
			pos = document.get_end_iter()
		capture.connect('stdout-line', capture_stdout_line_document, document, pos)
	else:
		capture.connect('stdout-line', capture_stdout_line_panel, panel)
		document.begin_user_action()

	capture.connect('stderr-line',   capture_stderr_line_panel,   panel)
	capture.connect('begin-execute', capture_begin_execute_panel, panel, node.get('label'))
	capture.connect('end-execute',   capture_end_execute_panel,   panel, view, 
	                output_type in ('new-document','replace-document'))
	
	# Run the command
	view.get_window(gtk.TEXT_WINDOW_TEXT).set_cursor(gdk.Cursor(gdk.WATCH))
	capture.execute()
	document.end_user_action()

def capture_stderr_line_panel(capture, line, panel):
	panel.write(line, panel.error_tag)

def capture_begin_execute_panel(capture, panel, label):
	panel['stop'].set_sensitive(True)
	panel.clear()
	panel.write(_("Running tool:"), panel.italic_tag);
	panel.write(" %s\n\n" % label, panel.bold_tag);

def capture_end_execute_panel(capture, exit_code, panel, view, update_type):
	panel['stop'].set_sensitive(False)
	
	if update_type:
		doc = view.get_buffer()
		start = doc.get_start_iter()
		end = start.copy()
		end.forward_chars(300)
		
		mtype = gnomevfs.get_mime_type_for_data(doc.get_text(start, end))
		languages_manager = gtksourceview.SourceLanguagesManager()
		language = languages_manager.get_language_from_mime_type(mtype)
		if language is not None:
			doc.set_language(language)
	
	view.get_window(gtk.TEXT_WINDOW_TEXT).set_cursor(gdk.Cursor(gdk.XTERM))
	view.set_cursor_visible(True)
	view.set_editable(True)

	if exit_code == 0:
		panel.write("\n" + _("Done.") + "\n", panel.italic_tag)
	else:
		panel.write("\n" + _("Exited") + ":", panel.italic_tag)
		panel.write(" %d\n" % exit_code, panel.bold_tag)

def capture_stdout_line_panel(capture, line, panel):
	panel.write(line)

def capture_stdout_line_document(capture, line, document, pos):
	document.insert(pos, line)
