#!/usr/bin/python

# Copyright (C) 2008 Valmantas Paliksa <walmis at balticum-tv dot lt>
# Copyright (C) 2008 Tadas Dailyda <tadas at dailyda dot com>
#
# Licensed under the GNU General Public License Version 3
#
# 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 3 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, see <http://www.gnu.org/licenses/>.
# 

import sys
from subprocess import *
import os.path
import pynotify
import dbus
import dbus.glib
import gobject
import gtk
import gtk.gdk

#support running uninstalled
_dirname = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
if os.path.exists(os.path.join(_dirname,"ChangeLog")):
	sys.path.insert(0, _dirname)

from blueman.Constants import *
from blueman.main.applet.Transfer import Transfer
from blueman.main.SignalTracker import SignalTracker
from blueman.Functions import *
from blueman.main.applet.BluezAgent import AdapterAgent
from blueman.main.applet.DbusService import DbusService
from blueman.main.Mechanism import Mechanism
from blueman.main.applet.NetworkManager import NetworkManager
from blueman.gui.applet.DiscvManager import DiscvManager

import blueman.plugins.applet
from blueman.plugins.AppletPlugin import AppletPlugin

import blueman.bluez as Bluez
from blueman.gui.CommonUi import show_about_dialog
import gettext
_ = gettext.gettext


from blueman.gui.applet.RecentConns import RecentConns

class BluemanApplet:

	def __init__(self):
		setup_icon_path()
		if not pynotify.init("Blueman"):
			dprint("Error: Failed to init pynotify")
		
		check_single_instance("blueman-applet")
		
		self.load_plugins()
		
		self.menu = gtk.Menu()
		self.Manager = None
		
		self.DbusSvc = DbusService(self)
		
		self.disc_item = create_menuitem(_("Make Discoverable"), get_icon("gtk-find", 16))
		self.turnoff_item = create_menuitem(_("Bluetooth Off"), get_icon("gtk-stop", 16))
		self.turnoff_item.props.tooltip_text = _("Turn off all adapters")
		self.turnoff_item.connect("activate", lambda x: self.on_bluetooth_toggled())
		self.bluetooth_off = False
		
		
		self.recent_item = create_menuitem(_("Recent Connections")+"...", get_icon("document-open-recent", 16))
		self.recent_menu = RecentConns(self)
		
		self.Transfer = None
		self.Signals = SignalTracker()
		self.Agents = []
		self.status_icon = gtk.StatusIcon()
		self.status_icon = gtk.status_icon_new_from_icon_name("blueman")
		self.status_icon.set_tooltip(_("Bluetooth applet"))
		self.status_icon.connect("popup-menu", self.on_popup_menu)
		
		def on_activate(status_icon):
			self.on_devices(None)
		
		self.status_icon.connect("activate", on_activate)
		
		self.build_popup_menu()
		
		#determine if icon should be visible (in case adapters are present)
		self.bus = dbus.SystemBus()
		self.bus.watch_name_owner("org.bluez", self.on_dbus_name_owner_change)

		self.sess_bus = dbus.SessionBus()
		self.sess_bus.watch_name_owner("org.openobex", self.on_obex_owner_changed)
			
		self.load_nap_settings()
		
		gobject.idle_add(self.try_start_ods)
		
		gtk.main()
	
	def try_start_ods(self):
		try:
			self.sess_bus.start_service_by_name("org.openobex")
		except dbus.DBusException, e:
			dprint("Could not acquire obex-data-server", e)		
	
	def load_plugins(self):
		path = os.path.dirname(blueman.plugins.applet.__file__)
		plugins = []
		for root, dirs, files in os.walk(path):
			for f in files:
				if f.endswith(".py") and not (f.endswith(".pyc") or f.endswith("_.py")):
					plugins.append(f[0:-3])
		plugins.sort()
		dprint(plugins)
		for plugin in plugins:
			try:
				__import__("blueman.plugins.applet.%s" % plugin, None, None, [])
			except ImportError, e:
				dprint("Unable to load %s plugin\n%s" % (plugin, e))
			

		for cls in AppletPlugin.__subclasses__():
			inst = cls()
			inst.on_load(self)


	def load_nap_settings(self):
		dprint("Loading NAP settings")
		def reply():
			pass
		def err(excp):
			lines = str(excp).splitlines()
			d = gtk.MessageDialog( None, buttons=gtk.BUTTONS_OK, type=gtk.MESSAGE_ERROR)
			d.props.text = _("Failed to apply network settings")
			d.props.secondary_text = lines[-1] + "\n\n"+_("You might not be able to connect to the Bluetooth network via this machine")
			d.run()
			d.destroy()
		
		m = Mechanism()
		m.NetworkSetup("reload", 0, "0", reply_handler=reply, error_handler=err)

	def on_obex_owner_changed(self, owner):
		dprint("obex owner changed:", owner)
		if owner != "":
			self.Transfer = Transfer(self)
		else:
			if self.Transfer:
				self.Transfer.DisconnectAll()
			self.Transfer = None
	
			
	def __setattr__(self, key, value):
		if key == "bluetooth_off":
			dprint("bt_off", value)
			if value:
				self.turnoff_item.get_child().set_markup(_("<b>Turn Bluetooth On</b>"))
				self.turnoff_item.set_image(gtk.image_new_from_pixbuf(get_icon("gtk-yes", 16)))
				self.DbusSvc.BluetoothStatusChanged(False)
			else:
				self.turnoff_item.get_child().set_markup(_("<b>Turn Bluetooth Off</b>"))
				self.turnoff_item.set_image(gtk.image_new_from_pixbuf(get_icon("gtk-stop", 16)))
				self.DbusSvc.BluetoothStatusChanged(True)
			if key in self.__dict__:
				dprint("off", self.__dict__[key], value)
				if self.__dict__[key] != value:
					adapters = self.Manager.ListAdapters()
					for adapter in adapters:
						adapter.SetProperty("Powered", not value)
					
		self.__dict__[key] = value
		
	def on_bluetooth_toggled(self):
		self.bluetooth_off = not self.bluetooth_off
	
	def manager_init(self):
		try:
			for agent in self.Agents:
				agent.Release()
			self.Agents = []

			self.Signals.DisconnectAll()
			self.Manager = Bluez.Manager("gobject")
			
			self.DiscvManager = DiscvManager(self)
			self.NM = NetworkManager(self)
			self.Signals.Handle("bluez", self.Manager, self.on_adapter_removed, "AdapterRemoved")
			self.Signals.Handle("bluez", self.Manager, self.on_adapter_added, "AdapterAdded")
			adapters = self.Manager.ListAdapters()
			self.status_icon.set_visible(adapters != [])
			for adapter in adapters:
				self.register_agent(adapter)
				props = adapter.GetProperties()
				if not props["Powered"]:
					self.bluetooth_off = True
					
			self.recent_menu.set_manager(self.Manager)
		
		except dbus.exceptions.DBusException, e:
			dprint(e)
			self.manager_deinit()
			dprint("Bluez DBus API not available. Listening for DBus name ownership changes")
	
	def manager_deinit(self):
		self.Signals.DisconnectAll()
		self.Manager = None
		self.DiscvManager = None
		self.NM = None
		self.status_icon.set_visible(False)		
	
	def register_agent(self, adapter):
		dprint("Registering agent")
		try:
			agent = AdapterAgent(self, adapter)
			adapter.RegisterAgent(agent, "DisplayYesNo")
			self.Agents.append(agent)

		except Exception, e:
			dprint("Failed to register agent")
			dprint(e)
		
	def on_dbus_name_owner_change(self, owner):
		dprint("org.bluez owner changed to", owner)
		if owner == "":
			self.manager_deinit()
		elif self.Manager == None:
			self.manager_init()
		
	def on_adapter_property_changed(self, key, value):
		if key == "Powered":
			if not value:
				print "Unpowered"
	
	def on_adapter_added(self, path):
		dprint("Adapter added ", path)
		def on_activate():
			dprint("Adapter activated")
			
			self.status_icon.set_visible(True)
			self.register_agent(adapter)
			if self.bluetooth_off:
				adapter.SetProperty("Powered", False)
				
			self.NM.update_status()

		adapter = Bluez.Adapter(path)
		wait_for_adapter(adapter, on_activate)
		#self.Signals.Handle(adapter, self.on_adapter_property_changed, "PropertyChanged")
		
	def on_adapter_removed(self, path):
		dprint("Adapter removed ", path)
		
		if self.Manager.ListAdapters() == []:
			self.status_icon.set_visible(False)
			
	def build_popup_menu(self):
		
		menu_items = []
		menu_items += [self.turnoff_item]
		menu_items += [self.disc_item]
		menu_items += [gtk.SeparatorMenuItem()]
		
		menu_items += [create_menuitem(_("Setup new device")+"...", get_icon("gtk-new", 16))]
		menu_items[-1].connect("activate", self.on_setup_new)
		menu_items += [gtk.SeparatorMenuItem()]
		menu_items += [create_menuitem(_("Send files to device")+"...", get_icon("blueman-send-file", 16))]
		menu_items[-1].connect("activate", self.on_send)

		
		if OBEX_BROWSE_AVAILABLE:
			menu_items += [create_menuitem(_("Browse files on device")+"...", get_icon("gtk-open", 16))]
			menu_items[-1].connect("activate", self.on_browse)
		
		menu_items += [gtk.SeparatorMenuItem()]
		
		menu_items += [self.recent_item]
		menu_items[-1].set_submenu(self.recent_menu)

		menu_items += [gtk.SeparatorMenuItem()]
		menu_items += [gtk.MenuItem(_("Devices")+"...", False)]
		menu_items[-1].connect("activate", self.on_devices)
		
		menu_items += [create_menuitem(_("Adapters")+"...", get_icon("blueman-device", 16))]
		menu_items[-1].connect("activate", self.on_adapters)
		
		menu_items += [create_menuitem(_("Local Services")+"...", get_icon("gtk-preferences", 16))]
		menu_items[-1].connect("activate", self.on_local_services)
		
		menu_items += [gtk.SeparatorMenuItem()]
		menu_items += [gtk.ImageMenuItem(gtk.STOCK_ABOUT)]
		menu_items[-1].connect("activate", self.on_about)
		
		
		for menu_item in menu_items:
			self.menu.append(menu_item)
			menu_item.show()
			
	def on_popup_menu(self, status_icon, button, activate_time):
		self.menu.popup(None, None, gtk.status_icon_position_menu,
						button, activate_time, status_icon)
	
	def on_setup_new(self, menu_item):
		sn = startup_notification("Bluetooth Assistant", _("Starting Bluetooth Assistant"), bin_name="blueman-assistant", icon="blueman")
		spawn("blueman-assistant", sn=sn)
		
	def on_send(self, menu_item):
		sn = startup_notification("Blueman Sendto", _("Starting File Sender"), bin_name="blueman-sendto", icon="blueman")
		spawn("blueman-sendto", sn=sn)
		
	def on_browse(self, menu_item):
		sn = startup_notification("Blueman Browse", _("Starting File Browser"), bin_name="blueman-browse", icon="blueman")
		spawn("blueman-browse", sn=sn)
		
	def on_devices(self, menu_item):
		sn = startup_notification("Blueman Manager", _("Starting Device Manager"), bin_name="blueman-manger", icon="blueman")
		spawn("blueman-manager", sn=sn)
		
	def on_adapters(self, menu_item):
		sn = startup_notification("Blueman Adapters", _("Starting Adapter Preferences"), bin_name="blueman-adapters", icon="blueman")
		spawn("blueman-adapters", sn=sn)
		
	def on_local_services(self, menu_item):
		sn = startup_notification("Blueman Services", _("Starting Service Preferences"), bin_name="blueman-services", icon="blueman")
		spawn("blueman-services", sn=sn)
		
	def on_about(self, menu_item):
		show_about_dialog("Blueman "+_("applet"))
	
	def show_notification(self, summary, message, timeout=-1, actions= None, actions_cb=None, pixbuf=None):
		def on_notification_closed(n):
			n.disconnect(closed_sig)
			if actions_cb:
				actions_cb(n, "closed")
		def on_action(*args):
			n.disconnect(closed_sig)
			actions_cb(*args)	
		
		n = pynotify.Notification(summary, message)
		if pixbuf:
			n.set_icon_from_pixbuf(pixbuf)
		
		if actions:
			for action in actions:
				n.add_action(action[0], action[1], on_action)
			n.add_action("default", "Default Action", on_action)
		closed_sig = n.connect("closed", on_notification_closed)
		if timeout != -1:
			n.set_timeout(timeout)
		if self.status_icon.get_visible():
			screen, area, orientation = self.status_icon.get_geometry()
			n.set_hint("x", area.x + area.width/2)
			n.set_hint("y", area.y + area.height/2)
		n.show()
		return n

BluemanApplet()

