#!/usr/bin/python

# This is a part of the third-party applets for Cairo-Dock
#
# Copyright : (C) 2010 by Fabounet
# E-mail : fabounet@users.berlios.de
#
# 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 sys
import os.path
import subprocess

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

import dbus
import gettext
from CDApplet import CDApplet, _

####################
### Applet class ###
####################
class Applet(CDApplet):
	def __init__(self):
		self.bus_name    = 'org.xchat.service'
		self.object_path = '/org/xchat/Remote'
		self.iface_name  = 'org.xchat.connection'
		self.bus = None
		self.xchat = None
		self.remote = None
		self.HookServerId = 0
		self.HookCmdId = 0
		self.HookPrintId = 0
		self.iSidConnect = 0
		self.iNbTries = 0
		self.iNbMsg = 0
		self.pMsgList = []
		self.iCurrentMsg = -1
		self.iNbUnread = 0
		self.bHasFocus = False
		self.cClass = ''
		self.bSilentMode = False
		self.pChanSilentMode = {}
		self.pChansList = []
		CDApplet.__init__(self)
	
	### Private Methods ###
	
	def on_name_owner_changed(self,connection_name):
		#print("on_name_owner_changed:",connection_name)
		if len(connection_name) == 0:
			self.xchat = None
			self.remote = None
		else:
			#print(">> connecting to xchat...")
			# get the xchat remote object on the bus
			bus_object = self.bus.get_object(connection_name, self.object_path)
			self.remote = dbus.Interface(bus_object, self.iface_name)
			# register to xchat as a plug-in
			path = self.remote.Connect ("xchat",
				"Cairo-Dock applet",
				"Cairo-Dock applet",
				"1.0")
			#print(">>> path :" + path)
			# get the plug-in object on the bus.
			proxy = self.bus.get_object('org.xchat.service', path)
			self.xchat = dbus.Interface(proxy, 'org.xchat.plugin')
			#print(">>> connected to " + self.xchat.GetInfo("network") + " (" + self.xchat.GetInfo("server") + ") as " + self.xchat.GetInfo("nick"))
			# connect to signals.
			#self.HookServerId = self.xchat.HookServer('PART', 0, 0)  # 0 <=> normal priority, 0 <=> let it pass (PRIVMSG, NOTICE, PART, etc)
			self.HookCmdId = self.xchat.HookCommand("cairo-dock", 0, "pouic pouic", 0)  # 0 <=> normal priority, 0 <=> let it pass
			self.HookPrintId = self.xchat.HookPrint("Channel Message", 0, 0)  # 0 <=> normal priority, 0 <=> let it pass
			self.xchat.connect_to_signal("ServerSignal", self.on_server_event)
			self.xchat.connect_to_signal("CommandSignal", self.on_command_catched)
			self.xchat.connect_to_signal("PrintSignal", self.on_print_event)
			self.xchat.connect_to_signal("UnloadSignal", self.on_unload_event)
	
	def list_channels(self):
		chans=[]
		channels = self.xchat.ListGet ("channels")
		while self.xchat.ListNext (channels):
			name = self.xchat.ListStr (channels, "channel")
			chans.append(name)
		self.xchat.ListFree (channels)
		return chans
	
	def set_nb_unread_msg(self,n):
		self.iNbUnread = n
		if n > 0:
			self.icon.SetQuickInfo(format(n,"d"))
		else:
			self.icon.SetQuickInfo('')

	def get_silent_mode_for_channel (self, cName, bCheckWithSharp):
		if cName in self.pChanSilentMode:
			return self.pChanSilentMode[cName]
		if bCheckWithSharp and '#'+cName in self.pChanSilentMode:
			return self.pChanSilentMode['#'+cName]
		return False
	
	#~ def list_channels_and_users(self):
		#~ channels = self.xchat.ListGet ("channels")
		#~ while self.xchat.ListNext (channels):
			#~ name = self.xchat.ListStr (channels, "channel")
			#~ print "------- " + name + " -------"
			#~ self.xchat.SetContext (xchat.ListInt (channels, "context"))
			#~ users = self.xchat.ListGet ("users")
			#~ while xchat.ListNext (users):
				#~ print "   Nick: " + self.xchat.ListStr (users, "nick")
			#~ self.xchat.ListFree (users)
		#~ self.xchat.ListFree (channels)
	
	##### Xchat Events #####
	def on_command_catched(self,words, words_eol, hook_id, ctx_id):
		if self.HookCmdId != hook_id:
			return
		#print(">>> CMD :" + words_eol[1])
		self.icon.ShowDialog("Yes, Master ?",4)

	def on_server_event(self, words, words_eol, hook_id, ctx_id):
		if self.HookServerId != hook_id:
			return
		if self.bSilentMode:
			return
		msg = words_eol[1]
		#"print(">>> EVENT :" + msg)
		if self.config['channel message']:
			self.icon.ShowDialog(msg,max(1,len(msg)/self.config['duration']))
		if self.config['animation type'] == 1:
			self.icon.Animate(self.config['msg animation'],10)
		elif self.config['animation type'] == 2:
			self.icon.DemandsAttention(True,self.config['msg animation'])

	def on_print_event(self, words, hook_id, ctx_id):
		if self.HookPrintId != hook_id:
			return
		if len(words) < 2:
			return
		#print(">>> PRINT :" + msg)
		if not self.bHasFocus:  # si l'utilisateur n'est pas devant xchat, on le notifie du message.
			self.xchat.SetContext(ctx_id) # needed for GetInfo
			if not self.bSilentMode and not self.get_silent_mode_for_channel (self.xchat.GetInfo("channel"), True):
				if self.config['channel message']:
					msg = words[0][3:] + ": " + words[1] # author (without strange chars): msg
					if self.iCurrentMsg != -1:
						msg = self.pMsgList[self.iCurrentMsg]+"\n------------------------------------------\n"+msg
					self.icon.ShowDialog(msg,max(2,len(msg)/self.config['duration']))
				if self.config['animation type'] == 1:
					self.icon.Animate(self.config['msg animation'],10000)
				elif self.config['animation type'] == 2:
					self.icon.DemandsAttention(True,self.config['msg animation'])
			self.set_nb_unread_msg(self.iNbUnread+1)
		
		if self.config['history'] != 0:  # on insere le message dans l'historique.
			self.pMsgList.insert(0,words[1])
			self.iNbMsg += 1
			if self.iNbMsg > self.config['history'] :
				del self.pMsgList[self.config['history']]
				self.iNbMsg -= 1
			if self.iCurrentMsg != -1:
				self.iCurrentMsg = min(self.iNbMsg-1, self.iCurrentMsg+1)  # on l'insere au debut donc ca decale le message courant.
		
	def on_unload_event():  # ne semble pas appele lorsque xchat quitte :-(
		#print(">>> xchat quits")
		del self.xchat
		self.xchat = None
		del self.pMsgList
		self.pMsgList = None
		self.iNbMsg = 0
		self.iCurrentMsg = -1
		self.set_nb_unread_msg(0)

	##### applet definition #####
	
	def get_config(self,keyfile):
		self.config['channel message'] = keyfile.getboolean('Configuration', 'channel message')
		self.config['animation type'] = keyfile.getint('Configuration', 'animation type')
		self.config['msg animation'] = keyfile.get('Configuration', 'msg animation')
		if self.config['msg animation'] == '':
			self.config['msg animation'] = 'default'
		duration = keyfile.getint('Configuration', 'duration')
		if duration == 0:
			self.config['duration'] = 16
		elif duration == 1:
			self.config['duration'] = 8
		else:
			self.config['duration'] = 4
		self.config['history'] = keyfile.getint('Configuration', 'history')
		channels = keyfile.get('Configuration', 'mute channels').split(';')
		for channel in channels:
			self.pChanSilentMode[channel] = True
		self.bSilentMode = keyfile.getboolean('Configuration', 'mute all')
	
	def begin(self):
		# connect to xchat
		self.bus = dbus.SessionBus()
		self.bus.watch_name_owner(self.bus_name, self.on_name_owner_changed)
		
		# control the appli
		process = os.popen("pgrep xchat-gnome").read().rstrip()
		if process != '':
			#print("xchat-gnome is already running")
			self.cClass = 'xchat-gnome'
		else:
			process = os.popen("pgrep xchat").read().rstrip()
			if process != '':
				#print("xchat is already running")
				self.cClass = 'xchat'
			else:
				path = os.popen("which xchat-gnome").read().rstrip()
				#print(">>> path to xchat-gnome : "+path)
				if path == '':
					self.cClass = 'xchat'
				else:
					self.cClass = 'xchat-gnome'
		#print(">>> class of the appli : "+self.cClass)
		self.icon.ControlAppli(self.cClass)
	
	def end(self):
		# disconnect from xchat
		if self.xchat == None:
			return
		if self.HookServerId != 0:
			self.xchat.Unhook(self.HookServerId)
			self.HookServerId = 0
		if self.HookCmdId != 0:
			self.xchat.Unhook(self.HookCmdId)
			self.HookCmdId = 0
		if self.HookPrintId != 0:
			self.xchat.Unhook(self.HookPrintId)
			self.HookPrintId = 0
		self.remote.Disconnect()  # n'a pas l'air de marcher...
	
	##### callbacks #####
	def on_click(self,iState):
		if self.xchat == None:  # pas connecte
			try:
				subprocess.Popen(self.cClass)
			except OSError:
				self.icon.ShowDialog("Couldn't launch "+self.cClass,5)
				return
		else:  # on montre la fenetre.
			if self.bHasFocus:  # la fenetre existe et a le focus.
				self.icon.ActOnAppli("minimize")  # on minimise la fenetre.
			else:  # soit la fenetre n'existe pas, soit elle n'a pas le focus.
				self.icon.ActOnAppli("show")
	
	def on_middle_click(self):
		if self.xchat == None:
			return	
		
		dialog_attributes = {
			"icon" : "gtk-italic",
			"message" : "Send a message on "+self.xchat.GetInfo("channel"),
			"buttons" : "ok;cancel"}
		widget_attributes = {
			"widget-type" : "text-entry"}
		self.icon.PopupDialog (dialog_attributes, widget_attributes)
	
	def on_build_menu(self):
		if self.xchat == None:
			return
		items=[]
		
		items=[{"type" : 1,  # 1 = sub-menu
			"label": _("Channels"),
			"icon" : "gtk-jump-to",
			"menu" : 0,
			"id"   : 1,
			"tooltip" : _("Switch the channel you'll send message to with the middle-click.")}]

		items.append({"type" : 1,  # 1 = sub-menu
			"label": _("Silent mode"),
			"icon" : "gtk-disconnect",
			"menu" : 0,
			"id"   : 2,
			"tooltip" : _("Switch the applet to silent mode (no pop-up on new message).")})
		
		self.pChansList = self.list_channels()
		i=3
		for c in self.pChansList:
			items.append({"label": c, "id" : i, "menu" : 1})
			i += 1

		items.append({"type" : 3,  # 3 = check-box
			"label": _("All channels"),
			"menu" : 2,
			"id"   : i,
			"state": self.bSilentMode,
			"tooltip" : _("Switch the applet to silent mode (no pop-up on new message).")})

		for c in self.pChansList:
			i += 1
			items.append({"type" : 3,  # 3 = check-box
				"label": c,
				"menu" : 2,
				"id"   : i,
				"state": self.get_silent_mode_for_channel (c, False)})

		self.icon.AddMenuItems(items)
	
	def on_menu_select(self,iNumEntry):
		iSilentId = len (self.pChansList) + 2 # two submenu before
		if iNumEntry == iSilentId: # silent all channels
			self.bSilentMode = not self.bSilentMode
		elif iNumEntry > iSilentId: # silent a channel
			channel = ''
			channel = self.pChansList[iNumEntry - iSilentId - 2]
			self.pChanSilentMode[channel] = not self.get_silent_mode_for_channel (channel, False)
		else:
			channel=''
			channel=self.pChansList[iNumEntry-3]
			#print(">>> channel " + channel)
			ctx = self.xchat.FindContext('', channel)
			if ctx == 0:
				#print("channel not found")
				return
			self.xchat.SetContext(ctx)
			self.xchat.Command("GUI FOCUS")  # pour donner le focus a l'onglet correspondant a notre contexte.
	
	def on_scroll(self,bDirectionUp):
		#print(">>> scroll",bDirectionUp,self.iNbMsg)
		if self.iNbMsg != 0 :
			if bDirectionUp:
				self.iCurrentMsg += 1
			else:
				self.iCurrentMsg -= 1
			if self.iCurrentMsg >= self.iNbMsg:
				self.iCurrentMsg = self.iNbMsg - 1
			elif self.iCurrentMsg < -1:
				self.iCurrentMsg = -1
			if self.iCurrentMsg >= 0:
				self.icon.ShowDialog(self.pMsgList[self.iCurrentMsg], 10)
		
	def on_answer_dialog(self,button,answer):
		if button == 0 or button == -1:  # ok or Enter
			#print(">>> answer :",answer)
			if answer == None or answer == '':
				return
			self.xchat.Command("say "+answer);

	def on_change_focus(self,has_focus):
		self.bHasFocus = has_focus
		if has_focus:
			#print(">>> got focus")
			if self.iNbUnread > 0 and self.config['animation type'] != 0:
				if self.config['animation type'] == 1:
					self.icon.Animate('',0)
				else:
					self.icon.DemandsAttention(False,'')
			self.set_nb_unread_msg(0)
			self.iCurrentMsg = -1

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