#!/usr/bin/python

# ubuntuone-client-applet - Tray icon applet for managing Ubuntu One
#
# Author: Rodney Dawes <rodney.dawes@canonical.com>
#
# Copyright 2009 Canonical Ltd.
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 3, as published
# by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranties of
# MERCHANTABILITY, SATISFACTORY QUALITY, 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/>.

from __future__ import with_statement

import pygtk
pygtk.require('2.0')
import gobject
import gtk
import os
import gettext
from ubuntuone import clientdefs

import dbus.service
from ConfigParser import ConfigParser
from dbus.exceptions import DBusException
from dbus.mainloop.glib import DBusGMainLoop
from xdg.BaseDirectory import xdg_config_home

DBusGMainLoop(set_as_default=True)

_ = gettext.gettext

APPLET_IFACE_NAME = "com.ubuntuone.ClientApplet"
APPLET_IFACE_CONFIG_NAME = APPLET_IFACE_NAME + ".Config"

DBUS_IFACE_NAME = "com.ubuntuone.SyncDaemon"
DBUS_IFACE_CONFIG_NAME = DBUS_IFACE_NAME + ".Config"

# Why thank you GTK+ for enforcing style-set and breaking API
RCSTYLE = """
style 'dialogs' {
  GtkDialog::action-area-border = 12
  GtkDialog::button-spacing = 6
  GtkDialog::content-area-border = 0
}
widget_class '*Dialog*' style 'dialogs'
"""

CONF_FILE = os.path.join(xdg_config_home, "ubuntuone", "ubuntuone-client.conf")

def dbus_async(*args):
      """Simple handler to make dbus do stuff async."""
      pass


class AppletConfigDialog(gtk.Dialog):
      """Preferences dialog."""

      def __init__(self, config=None, *args, **kw):
            """Initializes our config dialog."""
            super(AppletConfigDialog, self).__init__(*args, **kw)
            self.set_title(_("Ubuntu One Preferences"))
            self.set_has_separator(False)
            self.add_button(gtk.STOCK_CLOSE, gtk.RESPONSE_CLOSE)
            self.set_default_response(gtk.RESPONSE_CLOSE)
            self.set_icon_name("ubuntuone-client")

            self.connect("close", self.__handle_response, gtk.RESPONSE_CLOSE)
            self.connect("response", self.__handle_response)

            self.config = config

            self.bw_enabled = False
            self.up_limit = 2097152
            self.dn_limit = 2097152

            self.__bus = dbus.SessionBus()

            try:
                  client = self.__bus.get_object(DBUS_IFACE_NAME, "/config",
                                                 follow_name_owner_changes=True)
                  iface = dbus.Interface(client, DBUS_IFACE_CONFIG_NAME)
                  iface.get_throttling_limits(
                        reply_handler=self.__got_limits,
                        error_handler=self.__dbus_error)
                  iface.bandwidth_throttling_enabled(
                        reply_handler=self.__got_enabled,
                        error_handler=self.__dbus_error)
            except DBusException, e:
                  self.__dbus_error(e)

            # Timeout ID to avoid spamming DBus from spinbutton changes
            self.__update_id = 0

            self.load_config()

            self.__construct()

      def load_config(self):
            """Load the configuration, and initilize if empty."""
            if not os.path.isdir(os.path.dirname(CONF_FILE)):
                  os.makedirs(os.path.dirname(CONF_FILE))

            self.config = ConfigParser()
            self.config.read(CONF_FILE)

            if not self.config.has_section("ubuntuone"):
                  self.config.add_section("ubuntuone")

            if not self.config.has_option("ubuntuone", "show_applet"):
                  self.config.set("ubuntuone", "show_applet", "1")

            if not self.config.has_option("ubuntuone", "connect"):
                  self.config.set("ubuntuone", "connect", "0")

            if not self.config.has_option("ubuntuone", "connected"):
                  self.config.set("ubuntuone", "connected", "False")

            if not os.path.exists(CONF_FILE):
                  self.save_config()

      def save_config(self):
            """Write the configuration back to a file."""
            with open(CONF_FILE, "w+b") as f:
                  self.config.write(f)

      def quit(self):
            """Exit the main loop."""
            gtk.main_quit()

      def __dbus_error(self, error):
            """Error getting throttling config."""
            print repr(error)

      def __got_limits(self, limits):
            """Got the throttling limits."""
            self.up_limit = int(limits['upload'])
            self.dn_limit = int(limits['download'])
            self.up_spin.set_value(self.up_limit / 1024)
            self.dn_spin.set_value(self.dn_limit / 1024)

      def __got_enabled(self, enabled):
            """Got the throttling enabled config."""
            self.bw_enabled = bool(enabled)
            self.limit_check.set_active(self.bw_enabled)

      def __update_bw_settings(self):
            """Update the bandwidth throttling config in syncdaemon."""
            self.bw_enabled = self.limit_check.get_active()
            self.up_limit = self.up_spin.get_value_as_int() * 1024
            self.dn_limit = self.dn_spin.get_value_as_int() * 1024

            try:
                  client = self.__bus.get_object(DBUS_IFACE_NAME, "/config",
                                                 follow_name_owner_changes=True)
                  iface = dbus.Interface(client, DBUS_IFACE_CONFIG_NAME)
                  iface.set_throttling_limits(self.dn_limit, self.up_limit,
                        reply_handler=dbus_async,
                        error_handler=self.__dbus_error)
                  if self.bw_enabled:
                        iface.enable_bandwidth_throttling(
                              reply_handler=dbus_async,
                              error_handler=self.__dbus_error)
                  else:
                        iface.disable_bandwidth_throttling(
                              reply_handler=dbus_async,
                              error_handler=self.__dbus_error)
            except DBusException, e:
                  self.__dbus_error(e)

      def __handle_response(self, dialog, response):
            """Handle the dialog's response."""
            self.hide()
            self.config.set("ubuntuone", "show_applet",
                            self.show_menu.get_active())
            self.config.set("ubuntuone", "connect",
                            self.conn_menu.get_active())

            self.__update_bw_settings()
            self.save_config()
            gtk.main_quit()

      def __show_menu_changed(self, menu, data=None):
            """Update the config for showing the applet."""
            value = menu.get_active()
            self.config.set("ubuntuone", "show_applet", str(value))
            try:
                  client = self.__bus.get_object(APPLET_IFACE_NAME, "/config",
                                                 follow_name_owner_changes=True)
                  iface = dbus.Interface(client, APPLET_IFACE_CONFIG_NAME)
                  iface.set_visibility_config(value, reply_handler=dbus_async,
                                              error_handler=self.__dbus_error)
            except DBusException, e:
                  self.__dbus_error(e)

      def __conn_menu_changed(self, menu, data=None):
            """Update the config for showing the applet."""
            value = menu.get_active()
            self.config.set("ubuntuone", "connect", str(value))
            try:
                  client = self.__bus.get_object(APPLET_IFACE_NAME, "/config",
                                                 follow_name_owner_changes=True)
                  iface = dbus.Interface(client, APPLET_IFACE_CONFIG_NAME)
                  iface.set_connection_config(value, reply_handler=dbus_async,
                                              error_handler=self.__dbus_error)
            except DBusException, e:
                  self.__dbus_error(e)

      def __bw_limit_toggled(self, button, data=None):
            """Toggle the bw limit panel."""
            self.bw_enabled = self.limit_check.get_active()
            self.bw_table.set_sensitive(self.bw_enabled)
            try:
                  client = self.__bus.get_object(DBUS_IFACE_NAME, "/config",
                                                 follow_name_owner_changes=True)
                  iface = dbus.Interface(client, DBUS_IFACE_CONFIG_NAME)
                  iface.set_throttling_limits(self.dn_limit, self.up_limit,
                        reply_handler=dbus_async,
                        error_handler=self.__dbus_error)
                  if self.bw_enabled:
                        iface.enable_bandwidth_throttling(
                              reply_handler=dbus_async,
                              error_handler=self.__dbus_error)
                  else:
                        iface.disable_bandwidth_throttling(
                              reply_handler=dbus_async,
                              error_handler=self.__dbus_error)
            except DBusException, e:
                  self.__dbus_error(e)

      def __spinner_changed(self, button, data=None):
            """Remove timeout and add anew."""
            if self.__update_id != 0:
                  gobject.source_remove(self.__update_id)

            self.__update_id = gobject.timeout_add_seconds(
                  1, self.__update_bw_settings)

      def __construct(self):
            """Construct the dialog's layout."""
            area = self.get_content_area()

            vbox = gtk.VBox(spacing=12)
            vbox.set_border_width(12)
            area.add(vbox)
            vbox.show()

            # Put the first set of options in a table.
            table = gtk.Table(rows=2, columns=2)
            table.set_row_spacings(12)
            table.set_col_spacings(6)
            vbox.add(table)
            table.show()

            label = gtk.Label(_("_Show icon:"))
            label.set_use_underline(True)
            label.set_alignment(0, 0.5)
            table.attach(label, 0, 1, 0, 1)
            label.show()

            self.show_menu = gtk.combo_box_new_text()
            self.show_menu.connect("changed", self.__show_menu_changed)
            label.set_mnemonic_widget(self.show_menu)
            self.show_menu.append_text(_("Always"))
            self.show_menu.append_text(_("When updating"))
            self.show_menu.append_text(_("Never"))
            self.show_menu.set_active(self.config.getint("ubuntuone",
                                                         "show_applet"))
            table.attach(self.show_menu, 1, 2, 0, 1)
            self.show_menu.show()

            label = gtk.Label(_("Connect on start:"))
            label.set_use_underline(True)
            label.set_alignment(0, 0.5)
            table.attach(label, 0, 1, 1, 2)
            label.show()

            self.conn_menu = gtk.combo_box_new_text()
            self.conn_menu.connect("changed", self.__conn_menu_changed)
            label.set_mnemonic_widget(self.conn_menu)
            self.conn_menu.append_text(_("Automatically"))
            self.conn_menu.append_text(_("Remember last"))
            self.conn_menu.append_text(_("Never"))
            self.conn_menu.set_active(self.config.getint("ubuntuone",
                                                         "connect"))
            table.attach(self.conn_menu, 1, 2, 1, 2)
            self.conn_menu.show()

            # Bandwidth limiting
            self.limit_check = gtk.CheckButton(_("_Limit Bandwidth Usage"))
            self.limit_check.connect("toggled", self.__bw_limit_toggled)
            vbox.add(self.limit_check)
            self.limit_check.show()

            hbox = gtk.HBox(spacing=12)
            vbox.add(hbox)
            hbox.show()

            label = gtk.Label()
            hbox.add(label)
            label.show()

            # Now put the bw limit bits in a table too
            self.bw_table = gtk.Table(rows=2, columns=2)
            self.bw_table.set_row_spacings(6)
            self.bw_table.set_col_spacings(6)
            self.bw_table.set_sensitive(False)
            hbox.add(self.bw_table)
            self.bw_table.show()

            # Upload speed
            label = gtk.Label(_("Maximum _upload speed (KB/s):"))
            label.set_use_underline(True)
            label.set_alignment(0, 0.5)
            self.bw_table.attach(label, 0, 1, 0, 1)
            label.show()

            adjustment = gtk.Adjustment(value=2048.0, lower=0.0, upper=4096.0,
                                        step_incr=64.0, page_incr=128.0)
            self.up_spin = gtk.SpinButton(adjustment)
            self.up_spin.connect("value-changed", self.__spinner_changed)
            label.set_mnemonic_widget(self.up_spin)
            self.bw_table.attach(self.up_spin, 1, 2, 0, 1)
            self.up_spin.show()

            # Download speed
            label = gtk.Label(_("Maximum _download speed (KB/s):"))
            label.set_use_underline(True)
            label.set_alignment(0, 0.5)
            self.bw_table.attach(label, 0, 1, 1, 2)
            label.show()
            adjustment = gtk.Adjustment(value=2048.0, lower=0.0, upper=4096.0,
                                        step_incr=64.0, page_incr=128.0)
            self.dn_spin = gtk.SpinButton(adjustment)
            self.dn_spin.connect("value-changed", self.__spinner_changed)
            label.set_mnemonic_widget(self.dn_spin)
            self.bw_table.attach(self.dn_spin, 1, 2, 1, 2)
            self.dn_spin.show()


if __name__ == "__main__":
      gettext.bindtextdomain(clientdefs.GETTEXT_PACKAGE, clientdefs.LOCALEDIR)
      gettext.textdomain(clientdefs.GETTEXT_PACKAGE)

      gtk.rc_parse_string(RCSTYLE)

      try:
            dialog = AppletConfigDialog()
            dialog.show()
            gtk.main()
      except KeyboardInterrupt:
            pass
