#!/usr/bin/env python

# This file is part of Window-Switch.
# Copyright (c) 2009-2013 Antoine Martin <antoine@nagafix.co.uk>
# Window-Switch is released under the terms of the GNU GPL v3

import sys
import traceback
import os
import subprocess
from optparse import OptionParser

USE_XDG = False			#causes problems as it exits immediately...
XDG_FALLBACK = True		#but if everything else fails...
XDG_MIME = True			#but still use xdg for finding applications from mimetypes

debug = True
xdg_open = None
xdg_mime = None
if USE_XDG or XDG_FALLBACK or XDG_MIME:
	from winswitch.util.which import which
	xdg_open = which("xdg-open")
	xdg_mime = which("xdg-mime")
	from winswitch.util.common import is_valid_file
	if not is_valid_file(xdg_open):
		xdg_open = None


def get_command_for(filename, mime):
	print("mime_open.get_command_for(%s,%s)" % (filename, mime))
	command = xdg_mime_findmatch(mime)
	if command:
		return	command
	cap = mailcap_findmatches(filename, mime)
	print("mime_open.get_command_for(%s,%s) cap=%s" % (filename, mime, cap))
	if len(cap)==0:
		return	None
	(command, mappings) = cap[0]		#ie: ('xmpeg /tmp/tmp1223', {'view': 'xmpeg %s'})
	print("mime_open.get_command_for(%s,%s) command=%s, mappings=%s" % (filename, mime, command, mappings))
	return command

def xdg_mime_findmatch(mime):
	if not xdg_mime or not mime:
		return	None
	cmd = ["xdg-mime", "query", "default", mime]
	from winswitch.util.process_util import get_output
	code, out, err = get_output(cmd)
	print("mime_open.xdg_mime_findmatch(%s) xdg-mime returned %s" % (mime, code))
	if code!=0 or not out or err:
		return	None
	desktop_files = out.splitlines()
	print("mime_open.xdg_mime_findmatch(%s) xdg-mime recommends: %s" % (mime, desktop_files))
	dirsets = [os.environ.get("XDG_DATA_HOME", os.path.expanduser("~/.local/share")), os.environ.get("XDG_DATA_DIRS", "/usr/local/share:/usr/share")]
	for dirset in dirsets:
		for d in dirset.split(":"):
			if not d or not os.path.isdir(d):
				continue
			filename = os.path.join(d, "applications", desktop_files[0])
			if not os.path.exists(filename):
				continue
			print("mime_open.xdg_mime_findmatch(%s) found desktop file: %s" % (mime, filename))
			#load the desktop file:
			f = open(filename, 'rb')
			data = f.read()
			f.close()
			if not data:
				continue
			for line in data.splitlines():
				if line.startswith("Exec") and line.find("=")>0:
					command = line.split("=", 1)[1].split(" ", 1)[0]
					return which(command)
	return None

def mailcap_findmatches(filename, mime, max_matches=1):
	import mailcap
	plist=[]
	key='view'
	caps = mailcap.getcaps()
	matches = []

	entries = mailcap.lookup(caps, mime, key)
	for e in entries:
		if "needsterminal" in e:
			continue
		if 'test' in e:
			test = mailcap.subst(e['test'], filename, plist)
			if test and os.system(test) != 0:
				continue
		command = mailcap.subst(e[key], mime, filename, plist)
		print("mime_open.mailcap_findmatches(%s,%s) entry=%s" % (filename, mime, e))
		matches.append((command, e))
		if len(matches)>=max_matches:
			break
	return matches


def noquotes(s):
	#return	s.replace("'", "").replace('"', '')
	l = len(s)
	if l>=2:
		for test in ["'", '"']:
			if s[0] == test and s[l-1] == test:
				return	s[1:l-1]
	return	s


def message_server(command, args):
	print("message_server(%s,%s)" % (command, args))
	try:
		from winswitch.net.local_server_messenger import LocalServerMessenger
		from winswitch.util.format_util import format_message

		message = format_message(command, args)
		messenger = LocalServerMessenger([message])
		messenger.start(False)
	except:
		print("message_server(%s,%s)" % (command, args))
		traceback.print_exc(file=sys.stdout)

def do_warn_user(uuid, title, body):
	print("do_warn_user(%s,%s,%s)" % (uuid, title, body))
	if not uuid:
		return
	try:
		from winswitch.net.commands import SEND_MESSAGE
		message_server(SEND_MESSAGE, [uuid, title, body, uuid])
	except:
		print("do_warn_user(%s,%s,%s)" % (uuid, title, body))
		traceback.print_exc(file=sys.stdout)

def warn_user(uuid, title, body):
	from winswitch.util.common import no_newlines
	title = no_newlines(title)
	body = no_newlines(body)
	print("warn_user(%s,%s,%s)" % (uuid, title, body))
	if not uuid:
		return
	do_warn_user(uuid, title, body)

def server_set_command(display, mimetype, command):
	print("server_set_command(%s,%s,%s)" % (display,mimetype,command))
	if not display:
		return
	try:
		from winswitch.net.commands import SET_SESSION_COMMAND
		message_server(SET_SESSION_COMMAND, [display,command,mimetype])
	except:
		print("server_set_command(%s,%s,%s)" % (display,mimetype,command))
		traceback.print_exc(file=sys.stdout)


def get_session(session_file):
	if not session_file:
		return	None
	try :
		from winswitch.util.config import load_session_from_file
		from winswitch.objects.server_session import ServerSession

		session = load_session_from_file(session_file, False, ServerSession)
		print("mime_open.get_session(%s) loaded session: %s" % (session_file, session))
		return	session
	except:
		print("mime_open.get_session(%s)" % session_file)
		traceback.print_exc(file=sys.stdout)
	return	None



def exec_xdg_open(mimetype, filename, full_path, session):
	""" Open the file with xdg-open, either it works and never returns or it will return a non-zero value """
	command = [xdg_open, full_path]
	try:
		sys.stdout.flush()
		process = subprocess.Popen(command, stderr=subprocess.STDOUT)
		print("xdg_open(%s,%s,%s,%s) process=%s" % (mimetype, filename, full_path, session, process))
		if session:
			server_set_command(session.display, mimetype, "xdg-open %s" % filename)
		sys.stdout.flush()
		"""
			xdg-open does not wait for the process it launches,
			so if it has succeeded we should just wait here forever, ouch.
			see: http://calypso.tux.org/pipermail/xemacs-beta/2009-July/016690.html
		"""
		err = process.wait()
		if err==0:
			import time
			while True:
				time.sleep(1000*60*60)
		return err
	except Exception, e:
		print("xdg_open(%s,%s,%s,%s): %s" % (mimetype, filename, full_path, session, e))
		traceback.print_exc(file=sys.stdout)
		return	-1

def mime_open(filename, full_path, session):
	""" Opens the file with our mime code, this may warn the user in case of failure """
	sig = "mime_open.mime_open(%s,%s,%s)" % (filename, full_path, session)
	uuid = None
	if session:
		uuid = session.owner
	if os.path.isdir(full_path):
		warn_user(uuid, "Cannot open a directory!", "The file you specified (%s) is a directory!" % filename)
		print(sig+" cannot open directories!")
		return	-1

	if full_path.startswith("http:") or full_path.startswith("https:"):
		mime = "text/html"
	else:
		import mimetypes
		mimetypes.init()
		mime, mtype = None, None
		for strict in [True,False]:
			mtype = mimetypes.guess_type(full_path, False)
			print(sig+" strict=%s, mimetype=%s" % (strict, mtype))
			if mtype:
				(mime, _) = mtype

			print(sig+" mime=%s" % mime)
			if mime:
				break

	cmd = None
	if mime:
		cmd = get_command_for(full_path, mime)
		if cmd and xdg_open and cmd.startswith(xdg_open) and XDG_FALLBACK:
			""" detected xdg-open in mime database """
			return	exec_xdg_open(mime, filename, full_path, session)
	else:
		print(sig+" could not find mimetype for '%s'" % full_path)

	if not cmd:
		if XDG_FALLBACK and xdg_open:
			""" try xdg as fallback """
			print(sig+" trying with xdg-open")
			exec_xdg_open(mime, filename, full_path, session)
			#if xdg-open fails, the method will return, so we should still display the error:
		warn_user(uuid, "Unknown file type!", "The file you specified (%s) was not recognised. You may want to try launching the software manually and opening the file from there." % full_path)
		print(sig+" cannot open without knowing the mimetype!")
		return -1

	print(sig+" cmd=%s" % cmd)
	if not cmd:
		warn_user(uuid, "No appropriate program is installed!", "The file you specified (%s) is of type %s, but there is no software currently installed to open this type of file." % (full_path, mime))
		return -1

	split_cmd = cmd.split(" ")
	print(sig+" split cmd=%s" % split_cmd)
	command = []
	for s_cmd in split_cmd:
		command.append(noquotes(s_cmd))

	print(sig+" command=%s" % command)
	#update the session object (if any)
	if session:
		session.command = command[0]

	try:
		sys.stdout.flush()
		if session:
			server_set_command(session.display, mime, session.command)
		process = subprocess.Popen(command, stderr=subprocess.STDOUT)
		print(sig+" process=%s" % process)
		sys.stdout.flush()
	except Exception, e:
		print(sig+" exception suppressed")
		traceback.print_exc(file=sys.stdout)

		#Tell user:
		title = "Failed to open file"
		body = "%s" % e
		warn_user(uuid, title, body)
		return -1
	return 0



def open_file(filename, session_file):
	sig = "mime_open.open_file(%s,%s)" % (filename, session_file)
	print(sig)
	session = get_session(session_file)
	uuid = None
	if session:
		uuid = session.owner

	full_path = filename
	if not filename.startswith("http"):
		if filename.startswith("~"):
			full_path = os.path.expanduser(filename)
		if not full_path.startswith("/"):
			full_path = os.path.expanduser("~/") + full_path
		if not os.path.exists(full_path):
			warn_user(uuid, "File does not exist", "The file you tried to open: '%s', does not exist!" % filename)
			print(sig+" '%s' does not exist!" % full_path)
			return	-1

	if USE_XDG and xdg_open:
		exec_xdg_open(None, filename, full_path, session)
	else:
		mime_open(filename, full_path, session)



def main():
	parser = OptionParser()
	parser.add_option("-f", "--session-file", action="store", type="string", dest="session_file")
	(options, args) = parser.parse_args()
	print("mime_open.main() args=%s" % args)
	print("mime_open.main() options=%s" % options)

	index = 1
	for arg in args:
		open_file(arg, options.session_file)
		index += 1
	print("mime_open.main() ended")


if __name__ == "__main__":
	main()
