#!/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 BluezAgent
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.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.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.manager_init()

		self.sess_bus = dbus.SessionBus()
		self.sess_bus.watch_name_owner('org.openobex', self.on_obex_owner_changed)
		try:
			self.Transfer = Transfer(self)
		except:
			dprint("Unable to initialize obex-data-server")
			self.Transfer = None
			
		self.load_nap_settings()
		
		gtk.main()
	
	
	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):
		if owner != "" and not self.Transfer:
			self.Transfer = Transfer(self)
	
			
	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.remove_from_connection()
				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 = None
			self.DiscvManager = None
			self.NM = None
			self.status_icon.set_visible(False)
			dprint('Bluez DBus API not available. Listening for DBus name ownership changes')
			
	def register_agent(self, adapter):
		try:
			agent = BluezAgent(self, adapter.GetObjectPath())
			adapter.GetInterface().RegisterAgent(agent.dbus_path, '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 = None
			self.status_icon.set_visible(False)
		elif self.Manager == None:
			self.manager_init()
		
	def on_adapter_added(self, path):
		dprint('Adapter added ', path)
		def on_prop_change(key, value):
			if key == "Powered" and value:
				adapter.UnHandleSignal(on_prop_change, "PropertyChanged")
				
				self.status_icon.set_visible(True)
				self.register_agent(adapter)
				if self.bluetooth_off:
					adapter.SetProperty("Powered", False)

		adapter = Bluez.Adapter(path)
		adapter.HandleSignal(on_prop_change, "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 build_passkey_dialog(self, device_alias, dialog_msg, is_numeric):
		def on_insert_text(editable, new_text, new_text_length, position):
			if not new_text.isdigit():
				editable.stop_emission("insert-text")
		
		builder = gtk.Builder()
		builder.add_from_file(UI_PATH +"/applet-passkey.ui")
		dialog = builder.get_object("dialog")
		dialog.props.icon_name = "blueman"
		dev_name = builder.get_object("device_name")
		dev_name.set_markup("<b>"+device_alias+"</b>")
		msg = builder.get_object("message")
		msg.set_text(dialog_msg)
		pin_entry = builder.get_object("pin_entry")
		show_input = builder.get_object("show_input_check")
		if (is_numeric):
			pin_entry.set_max_length(6)
			pin_entry.set_width_chars(6)
			pin_entry.connect("insert-text", on_insert_text)
			show_input.hide()
		else:
			pin_entry.set_max_length(16)
			pin_entry.set_width_chars(16)
			pin_entry.set_visibility(False)
		show_input.connect("toggled", lambda x: pin_entry.set_visibility(x.props.active))
		accept_button = builder.get_object("accept")
		pin_entry.connect("changed", lambda x: accept_button.set_sensitive(x.get_text() != ''))
		
		return (dialog, pin_entry)
	
	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, 'default')
			
		
		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], actions_cb)
			n.add_action('default', 'Default Action', actions_cb)
		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()

