#!/usr/bin/python

# This is a part of the third-party applets for Cairo-Dock
#
# Copyright : (C) 2010 by ppmt and Tofe
#             (C) 2012 by Matthieu Baerts (matttbe) and the Cairo-Dock team
# E-mail : ppmt@glx-dock.org and chris.chapuis@gmail.com
#
#
# 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.
# http://www.gnu.org/licenses/licenses.html#GPL

####################
### dependancies ###
####################
from __future__ import print_function

import datetime
import subprocess

try:
	import gobject
except:
	from gi.repository import GObject as gobject

import dbus
from dbus.mainloop.glib import DBusGMainLoop
from CDApplet import CDApplet, _

# we support both 0.2 and 0.3 versions but we need to detect which one is available.
bIsGTG3 = True

#### transparent gtk window with text
from postit import TransparentPostIt

# the postit function:
def add_PostIt(task_text):
	postit = TransparentPostIt()
	postit.set_text(task_text)

DBusGMainLoop(set_as_default=True)

#########################
### Events from gtg ###
#########################

#This is where you put the event coming from GTG (signals)
def action_on_due_task():
	pass
	#print "A task is due!!!!"

####################
### Applet class ###
####################
class Applet(CDApplet):
	def __init__(self):
		self.gtg = None
		self.iSidConnect = 0
		self.iNbTries = 0
		self.cClass = ''
		self.bHasFocus = False
		# call high-level init
		CDApplet.__init__(self)
	
	def get_config(self,keyfile):
		self.config['remind message'] = keyfile.getboolean('Configuration', 'remind message')
		self.config['msg animation'] = keyfile.get('Configuration', 'msg animation')
		duration = keyfile.getint('Configuration', 'duration')
		# ce serait sympa d'avoir la taille du postit, mais je sais pas comment forcer la mise a jour du .conf de l'utilisateur...
		# self.config['postit size'] = keyfile.get('Configuration', 'postit size')
		if duration == 0:
			self.config['duration'] = 16
		elif duration == 1:
			self.config['duration'] = 8
		else:
			self.config['duration'] = 4
	
	def begin(self):
		self.connect_to_gtg()
		self.cClass = 'gtg'
		# print ">>> class of the appli : "+self.cClass
		self.icon.ControlAppli(self.cClass) #removed following fabounet advice
	
	def end(self):
		self.disconnect_from_gtg()
	
	def connect_to_gtg(self):
		# register a new plug-in in gtg.
		global bIsGTG3
		gtgbus = dbus.SessionBus()
		# print ">> connecting to GTG..."
		try:
			gtgproxy = gtgbus.get_object('org.gnome.GTG', '/org/gnome/GTG')
		except dbus.DBusException:
			print(">>> GTG 0.3 is not available")
			try:
				gtgproxy = gtgbus.get_object('org.GTG', '/org/GTG')
			except dbus.DBusException:
				print(">>> GTG 0.2 and 0.3 are not available on the bus")
				return
			bIsGTG3 = False
			print(">>> ... but GTG 0.2 is available")
		if bIsGTG3:
			self.gtg = dbus.Interface(gtgproxy, 'org.gnome.GTG')
			## TODO SIGNALS... ?
		else:
			self.gtg = dbus.Interface(gtgproxy, 'org.GTG')
			self.gtg.connect_to_signal("due_task", action_on_due_task)
		self.task_due(0)
	
	def disconnect_from_gtg(self):
		if self.gtg == None:
				return
	
	def list_tasks(self):
		list_task = []
		if bIsGTG3:
			gtg_tasks = self.gtg.GetTasksFiltered(['active']) ## ([]) => all ## , 'workable' => without categories
		else:
			gtg_tasks = self.gtg.get_tasks()

		if bIsGTG3:
			for t in gtg_tasks:
				task_content = t["id"],t["status"],t["title"],t["duedate"],t["startdate"],t["donedate"],t["tags"],t["text"],t["subtask"],t["parents"]
				list_task.append(task_content)
		else: #it is certainly 0.2.x ==> no parents
			subtask_parents = {}
			for t in gtg_tasks:
				task_content = t["id"],t["status"],t["title"],t["duedate"],t["startdate"],t["donedate"],t["tags"],t["text"],t["subtask"],subtask_parents.get(t["id"],[])
				for subtask in t["subtask"]:
					subtask_parents[subtask] = [t["id"]]
				list_task.append(task_content)

		return list_task

	##################################
	### callbacks on the main icon ###
	##################################
	def _try_connect(self):
		self.connect_to_gtg()
		if self.gtg == None:  # still not connected.
			self.iNbTries += 1
			if self.iNbTries == 5:  # give up after 5 tries.
				self.icon.ShowDialog(_("Couldn't connect to GTG :-(\nDid you install it?"),4)
				self.iSidConnect = 0
				return False
			return True
		else:  # connection established.
			self.iSidConnect = 0
			return False

	def on_click(self,iState):
		if self.gtg == None:  # not connected
			if self.iSidConnect == 0:  # and not in the process either
				try:
					subprocess.Popen(self.cClass)
				except OSError:
					self.icon.ShowDialog(_("Couldn't launch ")+self.cClass,5)
					return
				self.iNbTries = 0
				self.iSidConnect = gobject.timeout_add(1000,self._try_connect)
			else:  # Connection in progress
				self.icon.ShowDialog(_("Connecting to gtg, please wait ..."),4)
		else:  # let's show he window.
			if self.bHasFocus:  # the windows exists and has the focus
				#self.icon.ShowAppli(False)  # We minimise it.
				if bIsGTG3:
					self.gtg.IconifyTaskBrowser()  # We minimise it.
				else:
					self.gtg.hide_task_browser()  # We minimise it.
			else:  # either it doesn't exist or it doesn't have the focus.
				try:
					if bIsGTG3:
						self.gtg.ShowTaskBrowser()
					else:
						self.gtg.show_task_browser()   
					self.task_due(5)
					#self.icon.DemandsAttention(False,"")
				except dbus.DBusException: 
					print("gtg exited without telling us !") #so let's try to reconnect
					self.gtg = None
					self.connect_to_gtg()
					if self.gtg == None:  
						print("seems gtg is not running")
						p = subprocess.Popen("gtg")
						status = p.wait()
						self.iNbTries = 0
						self.iSidConnect = gobject.timeout_add(1000,self._try_connect)
					else:
						if bIsGTG3:
							self.gtg.ShowTaskBrowser()
						else:
							self.gtg.show_task_browser()
						#self.icon.DemandsAttention("False","")
						self.task_due(5)

	def on_middle_click(self):
		if self.gtg == None:
			return
		self.icon.AskText(_("Create a new task"), "");

	def _add_menu (self, menu_items, title, menu_id, current_menu_id, subtask_len, task_list_len):
		if (subtask_len > 0):
			menu_item_type = 1
		else:
			menu_item_type = 0

		menu_items.extend([{"type": menu_item_type,
											 "label" : title,
											 "menu": menu_id,
											 "id": current_menu_id}])
		if (subtask_len > 0):
			menu_items.extend([{"type": 0,
												 "label" : _("Open category's task"),
												 "menu": current_menu_id,
												 "id": task_list_len+current_menu_id}])
			menu_items.extend([{"type": 0,
												 "label" : _("Create post-it from this category"),
												 "menu": current_menu_id,
												 "id": 2*task_list_len+current_menu_id}])
			menu_items.extend([{"type": 2,
												 "menu": current_menu_id}])

	def on_build_menu(self):
		if (self.gtg == None):
			self.connect_to_gtg() # maybe GTG has been installed after (or gtg was launched with a delay at startup).
			if (self.gtg == None):
				return
		menu_items = []
		menu_ids = {}
		tasks_with_parents_not_found = []
		current_menu_id = 1
		task_list = self.list_tasks()
		for task_item in task_list:
			id,status,title,duedate,startdate,donedate,tags,text,subtask,parents = task_item
			menu_ids[str(id)] = current_menu_id
			#print id, title
			if (len(subtask) > 0):
				menu_item_type=1
			else:
				menu_item_type=0

			if (len(parents) == 0):
				menu_items.extend([{"type": menu_item_type,
								   "label" : title,
								   "menu": -1,
								   "id": current_menu_id}])
				if (len(subtask) > 0):
					menu_items.extend([{"type": 0,
														 "label" : _("Open category's task"),
														 "menu": current_menu_id,
														 "id": len(task_list)+current_menu_id}])
					menu_items.extend([{"type": 0,
														 "label" : _("Create post-it from this category"),
														 "menu": current_menu_id,
														 "id": 2*len(task_list)+current_menu_id}])
					menu_items.extend([{"type": 2,
														 "menu": current_menu_id}])
			else:
				parents_not_found = []
				for item_parent in parents:
					# it's possible to receive the child before its parent...
					if item_parent not in menu_ids:
						parents_not_found.append (item_parent)
					else:
						self._add_menu (menu_items, title, menu_ids[item_parent], current_menu_id, len (subtask), len (task_list))
				if len (parents_not_found) > 0:
					tasks_with_parents_not_found.append({'task':task_item, 'id':current_menu_id, 'parents_not_found':parents_not_found})
			current_menu_id = current_menu_id + 1

		# recheck if we didn't found parent of some tasks
		i = len (task_list) # to avoid infinite loop? but should not happen except if there is a bug in GTG... (wrong info in parent)
		while (len (tasks_with_parents_not_found) > 0 and i > 0):
			for package in tasks_with_parents_not_found: # task + id + parents not found
				id,status,title,duedate,startdate,donedate,tags,text,subtask,parents = package['task']

				for item_parent in package['parents_not_found']:
					# it's possible to receive the child before its parent...
					if item_parent in menu_ids: # we found the parent
						self._add_menu (menu_items, title, menu_ids[item_parent], package['id'], len (subtask), len (task_list))
						package['parents_not_found'].remove (item_parent)
				if (len (package['parents_not_found']) == 0): # we found all parents
					tasks_with_parents_not_found.remove (package)
				i = i - 1

		self.icon.AddMenuItems(menu_items)

	def task_due(self, duration):
		#task_title=[]
		duetasks = ""
		task_list = self.list_tasks()
		for task_item in task_list:
			id,status,title,duedate,startdate,donedate,tags,text,subtask,parents = task_item 
			today = str(datetime.date.today())
			if (duedate <= today and duedate != "" or duedate == "now"):
				if (duedate == today):
					duetasks=duetasks + _("The task: ") + title + _(" is due today") + "\n"
				if (duedate == "now"):
					duetasks=duetasks + _("The task: ") + title + _(" is due NOW") + "\n"
				if (duedate < today):
					duetasks = duetasks + _("The task: ") + title + _(" was due on ") + duedate + "\n"
				self.icon.ShowDialog(duetasks,duration)
				#self.icon.DemandsAttention("True","")

	def build_postit_from_task(self, postit_text, depth, id, mapping_task_id):
		# get the task item
		task_item = mapping_task_id[id]
		id,status,title,duedate,startdate,donedate,tags,text,subtask,parents = task_item 

		extended_postit_text = postit_text

		# add some space at the beginning
		if (depth > 0):
			for i in range(depth):
				extended_postit_text += ' '
			extended_postit_text += '`- '

	  # add the text itself
		extended_postit_text += title
		extended_postit_text += '\n'

		# for all the children, do the same at depth+3
		for child_task in subtask:
			extended_postit_text = self.build_postit_from_task(extended_postit_text, depth+3, child_task, mapping_task_id)
		
		return extended_postit_text

	def on_menu_select(self,iMenuID):
		# print ">>> menu select",iMenuID
		task_list = self.list_tasks()
		
		if (iMenuID > len(task_list) and iMenuID < 2*len(task_list) ):
			iMenuID = iMenuID - len(task_list)
			
		if (iMenuID < len(task_list)):
			task_item = task_list [iMenuID-1]
			id,status,title,duedate,startdate,donedate,tags,text,subtask,parents = task_item
			if bIsGTG3:
				self.gtg.OpenTaskEditor(id);
			else:
				self.gtg.open_task_editor(id)
		else:
			# task_item=task_list[iMenuID-2*len(task_list)-1]
			# prepare a mapping helper for the recursion
			mapping_task_id = {}
			for task_item in task_list:
				mapping_task_id[task_item[0]] = task_item
			postit_text = ""
			postit_text = self.build_postit_from_task(postit_text, 0, task_list[iMenuID - 2 * len(task_list) - 1][0], mapping_task_id)
			add_PostIt(postit_text)
		
	def on_answer_dialog(self,button, answer):
		#print ">>> answer :",answer
		if answer == None or answer == '':
			return
		if bIsGTG3:
			self.gtg.OpenNewTask(answer, "");
		else:
			self.gtg.open_new_task(answer,"");

	def on_change_focus(self,has_focus):
		self.bHasFocus = has_focus


############
### main ###
############
if __name__ == '__main__':
	Applet().run()
