#!/usr/bin/env python
# -*- coding: UTF-8 -*-
###
#
# Weather wallpaper is the legal property of Raul Gonzalez Duque <zootropo@gmail.com>
# Copyright (c) 2007 Raul Gonzalez Duque
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 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 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, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
###

try:
    import gi
    from gi.repository import Gio
except ImportError:
    pass
finally:
    import ConfigParser
    import gettext
    import gtk
    import os
    import pygtk
    import pymetar
    import sys
    import threading
    import time
    import urllib2

__version__ = "0.2.0"


class PreferencesDialog(gtk.Dialog):
    """Dialog box which displays the preferences."""
    def __init__(self):
        gtk.Dialog.__init__(self)
        self.add_button(gtk.STOCK_OK, gtk.RESPONSE_OK)
        self.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
        self.set_title(_("Preferences"))
        self.set_size_request(400, 370)
        self.connect("response", self.on_dialog_response)

        vbox = gtk.VBox()

        # Location section
        title = gtk.Label()
        title.set_markup("<span size=\"large\" weight=\"bold\">" + _("Location") + "</span>")
        hbox = gtk.HBox()
        hbox.pack_start(title, expand=False, fill=True, padding=2)
        vbox.pack_start(hbox, expand=False, fill=True, padding=0)

        help = gtk.Label()
        help.set_markup("<span size=\"small\">" + _("ICAO Location Indicator for your city which can be found at") + "</span>")
        help.set_line_wrap(True)
        hbox = gtk.HBox()
        hbox.pack_start(help, expand=False, fill=False, padding=5)
        vbox.pack_start(hbox, expand=False, fill=False, padding=0)
        url = gtk.LinkButton("http://www.nws.noaa.gov/tg/siteloc.shtml", _("NOAA webpage"))
        url.connect("clicked", self.open_web) 
        hbox = gtk.HBox()
        hbox.pack_start(url, expand=False, fill=False, padding=0)
        vbox.pack_start(hbox, expand=False, fill=False, padding=0)

        lbl_location = gtk.Label(_("ICAO: "))
        self.entry_location = gtk.Entry()
        self.entry_location.set_text(station_id)
        hbox = gtk.HBox()
        hbox.pack_start(lbl_location, expand=False, fill=False, padding=5)
        hbox.pack_start(self.entry_location, expand=False, fill=False, padding=0)
        vbox.pack_start(hbox)


        # Units section
        title = gtk.Label()
        title.set_markup("<span size=\"large\" weight=\"bold\">" + _("Units") + "</span>")
        hbox = gtk.HBox()
        hbox.pack_start(title, expand=False, fill=False, padding=2)
        vbox.pack_start(hbox, expand=False, fill=False, padding=0)

        help = gtk.Label()
        help.set_markup("<span size=\"small\">" + _("System of units you want to use. With Metric, units will be displayed using °C and Km; with Imperial, °F and Mi.") + "</span>")
        help.set_line_wrap(True)
        hbox = gtk.HBox()
        hbox.pack_start(help, expand=False, fill=False, padding=5)
        vbox.pack_start(hbox, expand=False, fill=False, padding=0)

        self.metric = gtk.RadioButton(group=None, label="Metric")
        self.metric.set_active(unit_system == "metric")
        self.imperial = gtk.RadioButton(group=self.metric, label="Imperial")
        self.imperial.set_active(unit_system == "imperial")
        hbox = gtk.HBox()
        hbox.pack_start(self.metric, expand=False, fill=False, padding=5)
        hbox.pack_start(self.imperial, expand=False, fill=False, padding=0)
        vbox.pack_start(hbox, expand=False, fill=False, padding=0)

        hbox = gtk.HBox()
        vbox.pack_start(hbox, expand=True, fill=True, padding=0)


        # Offset
        title = gtk.Label()
        title.set_markup("<span size=\"large\" weight=\"bold\">" + _("Offset") + "</span>")
        hbox = gtk.HBox()
        hbox.pack_start(title, expand=False, fill=False, padding=2)
        vbox.pack_start(hbox, expand=False, fill=False, padding=0)

        help = gtk.Label()
        help.set_markup("<span size=\"small\">" + _("With this setting you can change the position of the letters on the screen. A positive number will move the text to the right (x) or bottom (y). A negative one, to the left (x) or top (y).") + "</span>")
        help.set_line_wrap(True)
        hbox = gtk.HBox()
        hbox.pack_start(help, expand=False, fill=False, padding=5)
        vbox.pack_start(hbox, expand=False, fill=False, padding=0)

        lbl_offset_x = gtk.Label(_("Offset x: "))
        self.entry_offset_x = gtk.Entry()
        self.entry_offset_x.set_text(str(offset_x))
        hbox = gtk.HBox()
        hbox.pack_start(lbl_offset_x, expand=False, fill=False, padding=5)
        hbox.pack_start(self.entry_offset_x, expand=False, fill=False, padding=0)
        vbox.pack_start(hbox)

        lbl_offset_y = gtk.Label(_("Offset y: "))
        self.entry_offset_y = gtk.Entry()
        self.entry_offset_y.set_text(str(offset_y))
        hbox = gtk.HBox()
        hbox.pack_start(lbl_offset_y, expand=False, fill=False, padding=5)
        hbox.pack_start(self.entry_offset_y, expand=False, fill=False, padding=0)
        vbox.pack_start(hbox)


        # Proxy
        title = gtk.Label()
        title.set_markup("<span size=\"large\" weight=\"bold\">" + _("Proxy") + "</span>")
        hbox = gtk.HBox()
        hbox.pack_start(title, expand=False, fill=False, padding=2)
        vbox.pack_start(hbox, expand=False, fill=False, padding=0)

        help = gtk.Label()
        help.set_markup("<span size=\"small\">" + _("If you connect to the Internet using a proxy specify it here.") + "</span>")
        help.set_line_wrap(True)
        hbox = gtk.HBox()
        hbox.pack_start(help, expand=False, fill=False, padding=5)
        vbox.pack_start(hbox, expand=False, fill=False, padding=0)

        lbl_proxy = gtk.Label(_("Proxy: "))
        self.entry_proxy = gtk.Entry()
        self.entry_proxy.set_text(http_proxy)
        hbox = gtk.HBox()
        hbox.pack_start(lbl_proxy, expand=False, fill=False, padding=5)
        hbox.pack_start(self.entry_proxy, expand=False, fill=False, padding=0)
        vbox.pack_start(hbox)


        hbox = gtk.HBox()
        vbox.pack_start(hbox, expand=True, fill=True, padding=0)

        # Scrolled window
        scrolled_window = gtk.ScrolledWindow()
        scrolled_window.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
        scrolled_window.add_with_viewport(vbox)
        self.vbox.pack_start(scrolled_window, expand=True, fill=True, padding=0)
        self.show_all()
        self.run()
        self.destroy()

    def open_web(self, widget):
        """Called when the user clicks on the link button. Opens NOAA webpage."""
        import webbrowser
        webbrowser.open_new(widget.get_uri())

    def on_dialog_response(self, widget, response):
        """When the user closes the dialog by selecting OK, update the preferences."""
        if response == gtk.RESPONSE_OK:
            global station_id, unit_system, http_proxy, offset_x, offset_y
            # Get the new values
            new_location = self.entry_location.get_text()
            if self.metric.get_active():
                new_unit = "metric"
            else:
                new_unit = "imperial"
            new_proxy = self.entry_proxy.get_text()
            try:
                new_offset_x = float(self.entry_offset_x.get_text())
            except:
                new_offset_x = offset_x
            try:
                new_offset_y = float(self.entry_offset_y.get_text())
            except:
                new_offset_y = offset_y

            # If the user changed something update the conf file and the vars
            # and wake up the updater
            if (new_location != station_id or new_unit != unit_system or 
                new_proxy != http_proxy or new_offset_x != offset_x or
                new_offset_y != offset_y):
                cfg.set("General", "station id", new_location)
                cfg.set("General", "unit system", new_unit)
                cfg.set("General", "proxy", new_proxy)
                cfg.set("General", "offset_x", str(new_offset_x))
                cfg.set("General", "offset_y", str(new_offset_y))
                cfg.write(open(os.path.expanduser("~") + "/.weather-wallpaper/conf", "w"))
                station_id = new_location
                unit_system = new_unit
                http_proxy = new_proxy
                offset_x = new_offset_x
                offset_y = new_offset_y
                self.destroy()
                updater.stop_event.set()




class MyAboutDialog(gtk.AboutDialog):
    def __init__(self):
        super(MyAboutDialog, self).__init__()

        self.set_name("Weather wallpaper")
        self.set_comments("Wallpaper with the current weather!")
        self.set_version(__version__)
        self.set_copyright("Copyright © 2007 Raul Gonzalez Duque")
        self.set_license(file(os.path.join(path, "COPYING"), "r").read())
        self.set_website("http://mundogeek.net/")
        self.set_website_label("Mundo geek")
        self.set_authors([file(os.path.join(path, "AUTHORS"), "r").read()])
        self.set_translator_credits(file(os.path.join(path, "TRANSLATORS"), "r").read())
        self.set_artists(["Icons by the Tango Desktop Project", "Fog icons by Pepetps"])

        self.set_logo(gtk.gdk.pixbuf_new_from_file(os.path.join(path, "images", "snow.svg")))

        self.set_modal(True)
        self.show_all()
        self.run()
        self.destroy()


class Updater(threading.Thread):
    """Thread which updates the status icon and the wallpaper every hour"""
    def __init__(self):
        """Constructor, to set the initial values for the variables"""
        self.stop_event = threading.Event()
        self.stop = False
        threading.Thread.__init__(self)
        # Desktop wallpaper schema name.
        self.SCHEMA = 'org.gnome.desktop.background'
        # URI to use for the background image. Note that the backend only supports local (file://) URIs.
        self.KEY_URI = 'picture-uri'
        # Determines how the image set by wallpaper_filename is rendered. Possible values are "none",
        # "wallpaper", "centered", "scaled", "stretched", "zoom", "spanned".
        self.KEY_OPTS = 'picture-options'
        # How to shade the background color. Possible values are "horizontal", "vertical", and "solid".
        self.KEY_SHADING = 'color-shading-type'
        # Left or Top color when drawing gradients, or the solid color.
        self.KEY_PRIMARY = 'primary-color'
        # Right or Bottom color when drawing gradients, not used for solid color.
        self.KEY_SECOND = 'secondary-color'

    def run(self):
        """Execution loop for the thread. Creates and sets a new wallpaper with the info downloaded from NOAA"""
        while not self.stop:
            self.stop_event = threading.Event()
            statusIcon.set_from_stock(gtk.STOCK_REFRESH)
            statusIcon.set_tooltip(_("Retrieving report"))

            # The station ID can be found here: http://www.nws.noaa.gov/tg/siteloc.shtml
            fetcher = pymetar.ReportFetcher(station_id)
            parser = pymetar.ReportParser()
            try:
                report = fetcher.FetchReport(proxy=http_proxy)
            except (urllib2.URLError, pymetar.NetworkException):
                statusIcon.set_tooltip(_("There is a network problem. Trying again in 1 minute."))
                print _("There is a network problem. Trying again in 1 minute.")
                # Wait until the timeout elapses (1 minute) or until the stop
                # event is set by another thread
                self.stop_event.wait(60)
                self.run()
                return

            report = parser.ParseReport(report)

            if not report.valid:
                print _("The downloaded report is not valid")
                exit()

            hour = int(time.ctime().split()[3].split(":")[0])
            image = report.pixmap

            # Before sunrise (4:00 - 4:59)
            if hour >= 4 and hour < 5:
                if image == "sun":
                    image= "moon"
                if image == "suncloud":
                    image = "mooncloud"
                if image == "fog":
                    image = "moonfog"
                foreground = "#1E5073"
                background = "#1E5073"

            # Sunrise (5:00 - 5:59)
            if hour >= 5 and hour < 6:
                foreground = "#666666"
                background = "#6E98AE"

            # Before afternoon (6:00 - 13:59)
            if hour >= 6 and hour < 14:
                foreground = "#6E98AE"
                background = "#97BFCB"

            # Afternoon (14:00 - 19:59)
            if hour >= 14 and hour < 20:
                foreground = "#6382b9"
                background = "#8fb6ff"

            # Twilight (20:00 - 21:59)
            if hour >= 20 and hour < 22:
                if image == "sun":
                    image= "moon"
                if image == "suncloud":
                    image = "mooncloud"
                if image == "fog":
                    image = "moonfog"
                foreground = "#333333"
                background = "#694174"

            # Night (22:00 - 3:59)
            if hour >= 22 or hour < 4:
                foreground = "#000000"
                background = "#33436a"
                if image == "sun":
                    image = "moon"
                if image == "suncloud":
                    image = "mooncloud"
                if image == "fog":
                    image = "moonfog"

            original_file = os.path.join(path, "images", "%s.png" % image)
            if not os.path.exists(original_file):
                image = "None"

            if unit_system.lower() == "metric":
                temperature_num = report.getTemperatureCelsius()
                visibility_num = report.getVisibilityKilometers()
                dewpoint_num = report.getDewPointCelsius()
                wind_speed_num = report.getWindSpeed()

                if not temperature_num:
                    temperature = ""
                else:
                    temperature = "%d°C" % temperature_num

                if not visibility_num:
                    visibility = ""
                else:
                    visibility = "%.2fKm" % visibility_num

                if not dewpoint_num:
                    dewpoint = ""
                else:
                    dewpoint = "%d°C" % dewpoint_num

                if not wind_speed_num:
                    wind_speed = ""
                else:
                    wind_speed = "%.2fm/s" % wind_speed_num
            else:
                temperature_num = report.getTemperatureFahrenheit()
                visibility_num = report.getVisibilityMiles()
                dewpoint_num = report.getDewPointFahrenheit()
                wind_speed_num = report.getWindSpeedMilesPerHour()

                if not temperature_num:
                    temperature = ""
                else:
                    temperature = "%d°F" % temperature_num

                if not visibility_num:
                    visibility = ""
                else:
                    visibility = "%.2fMi" % visibility_num

                if not dewpoint_num:
                    dewpoint = ""
                else:
                    dewpoint = "%d°F" % dewpoint_num

                if not wind_speed_num:
                    wind_speed = ""
                else:
                    wind_speed = "%.2fmph" % wind_speed_num


            humidity_num = report.getHumidity()
            if not humidity_num:
                humidity = ""
            else:
                humidity = ("%d" % humidity_num) + "%"

            pressure_num = report.getPressure()
            if not pressure_num:
                pressure = ""
            else:
                pressure = "%.2fhPa" % pressure_num

            if not wind_speed:
                wind = ""
            else:
                wind = wind_dirs[str(report.getWindCompass())] + _(" at ") + wind_speed

            input_img = os.path.join(path, "images", image + ".svg")

            cmd = "inkscape --without-gui --export-background-opacity=0 \
                    --export-width=%s --export-height=%s --file=%s \
                    --export-png=%s" % (height, height, input_img, output_img)
            os.popen(cmd)

            cmd = "montage %s -background none -geometry +%s+0 %s" % (output_img, width - height, output_img)
            os.popen(cmd)

            report_str = report.getStationCity() + "\n"
            from time import strftime
            report_str += str(strftime("%a, %d %b %Y %H:%M")) + "\n\n"
            if temperature:
                report_str += _("Temperature: ") + "%s\n" % (temperature)
            if humidity:
                report_str += _("Humidity: ") + "%s\n" % (humidity)
            if visibility:
                report_str += _("Visibility: ") + "%s\n" % (visibility)
            if dewpoint:
                report_str += _("Dew point: ") + "%s\n" % (dewpoint)
            if wind:
                report_str += _("Wind: ") + "%s\n" % (wind)
            if pressure:
                report_str += _("Pressure: ") + "%s\n" % (pressure)
            report_str = report_str.replace("\'","\\\'")

            x = width + offset_x
            y = height - (height * 0.2) + offset_y
            cmd = "convert %s -fill white -pointsize 14 -stroke \#777 -strokewidth 1 -draw \"text %s,%s '%s'\" -stroke none -draw \"text %s,%s '%s'\" %s" % (output_img, x, y, report_str, x, y, report_str, output_img)
            os.popen(cmd)

            # If the user is running Gnome
            if os.environ.has_key('GNOME_DESKTOP_SESSION_ID'):
                try:
                    # If running Gnome 3.x
                    gsettings = Gio.Settings.new(self.SCHEMA)
                    gsettings.set_string(self.KEY_URI, "file://" + output_img)
                    gsettings.set_string(self.KEY_OPTS, "spanned")
                    gsettings.set_string(self.KEY_SHADING, "vertical")
                    gsettings.set_string(self.KEY_PRIMARY, foreground)
                    gsettings.set_string(self.KEY_SECOND, background)
                except NameError:
                    pass
                finally:
                    # If running Gnome 2.x
                    import gconf
                    client = gconf.client_get_default()
                    client.set_string("/desktop/gnome/background/picture_filename", output_img)
                    client.set_string("/desktop/gnome/background/picture_options", "centered")
                    client.set_string("/desktop/gnome/background/color_shading_type", "vertical-gradient")
                    client.set_string("/desktop/gnome/background/primary_color", foreground)
                    client.set_string("/desktop/gnome/background/secondary_color", background)
            # If the user is running KDE
            elif os.environ.has_key('KDE_FULL_SESSION'):
                cmd = "dcop kdesktop KBackgroundIface setWallpaper \"%s\" 1 " % (output_img)
                os.popen(cmd)
                cmd = "dcop kdesktop KBackgroundIface setColor \"%s\" false" % (background)
                os.popen(cmd)
                cmd = "dcop kdesktop KBackgroundIface setColor \"%s\" true" % (foreground)

            statusIcon.set_from_file(original_file)
            statusIcon.set_tooltip("%s (%s)" % (report.getStationCity(), temperature))

            # Wait until the timeout elapses (30 minutes) or until the stop event
            # is set by another thread
            self.stop_event.wait(1800)


def quit(widget, data=None):
    """Exists the gtk main loop and sets the stop event for the updater thread
    to stop"""
    gtk.main_quit()
    updater.stop = True
    updater.stop_event.set()


def popup_menu(widget, button, time, data=None):
    """Called whenever the user clicks on the status icon"""
    if button == 3:
        if data:
            data.show_all()
            data.popup(None, None, None, 3, time)
    pass


def show_about(widget, data=None):
    """Shows the about dialog"""
    about = MyAboutDialog()


def show_preferences(widget, data=None):
    preferences = PreferencesDialog()


def refresh(widget):
    updater.stop_event.set()



gettext.install("weather-wallpaper")


screen = gtk.gdk.display_get_default().get_default_screen()
screen_size = screen.get_monitor_geometry(0)
width = screen_size.width
height = screen_size.height


default_location = "LEGT"
default_units = "metric"

wind_dirs = {"N":_("North"), "S":_("South"), "E":_("East"), "W":_("West"),
        "NE":_("Northwest"), "NW":_("Northeast"), "SE":_("Southeast"),
        "SW":_("Southwest"), "NNE":_("North Northeast"),
        "NNW":_("North Northwest"), "SSE":_("South Southeast"),
        "SSW":_("South Southwest"), "ENE":_("East Northeast"),
        "ESE":_("East Southeast"), "WNW":_("West Northwest"),
        "WSW":_("West Southwest"), "None":_("None")}

output_img = os.path.expanduser("~") + "/.weather-wallpaper/.wallpaper.png"

path = os.path.abspath(os.path.join(os.path.dirname(sys.argv[0]), "..", "share",
                       "weather-wallpaper"))

# Read the configuration values from the config file. If the file does not exist
# create it with the default values
cfg = ConfigParser.SafeConfigParser()

if not os.path.exists(os.path.expanduser("~") + "/.weather-wallpaper/"):
    os.mkdir(os.path.expanduser("~") + "/.weather-wallpaper/")

if not os.path.exists(os.path.expanduser("~") + "/.weather-wallpaper/conf"):
    cfg.add_section("General")
    cfg.set("General", "station id", default_location)
    cfg.set("General", "unit system", default_units)
    cfg.set("General", "proxy", "")
    cfg.set("General", "offset_x", "0")
    cfg.set("General", "offset_y", "0")
    cfg.write(open(os.path.expanduser("~") + "/.weather-wallpaper/conf", "w"))

cfg.readfp(file(os.path.expanduser("~") + "/.weather-wallpaper/conf"))

try:
    station_id = cfg.get("General", "station id")
except:
    station_id = default_location

try:
    unit_system = cfg.get("General", "unit system")
except:
    unit_system = default_units

try:
    http_proxy = cfg.get("General", "proxy")
except:
    http_proxy = ""

try:
    offset_x = float(cfg.get("General", "offset_x"))
except:
    offset_x = 0.0

try:
    offset_y = float(cfg.get("General", "offset_y"))
except:
    offset_y = 0.0


statusIcon = gtk.StatusIcon()

try:
    # Run the thread which updates the wallpaper
    updater = Updater()
    updater.start()

    statusIcon.set_from_stock(gtk.STOCK_REFRESH)
    menu = gtk.Menu()

    menuItem = gtk.ImageMenuItem(gtk.STOCK_REFRESH)
    menuItem.connect("activate", refresh)
    menu.append(menuItem)

    sep = gtk.SeparatorMenuItem()
    menu.append(sep)

    menuItem = gtk.ImageMenuItem(gtk.STOCK_PREFERENCES)
    menuItem.connect("activate", show_preferences)
    menu.append(menuItem)

    menuItem = gtk.ImageMenuItem(gtk.STOCK_ABOUT)
    menuItem.connect("activate", show_about)
    menu.append(menuItem)

    sep = gtk.SeparatorMenuItem()
    menu.append(sep)

    menuItem = gtk.ImageMenuItem(gtk.STOCK_QUIT)
    menuItem.connect("activate", quit, statusIcon)
    menu.append(menuItem)

    statusIcon.connect("activate", show_preferences)
    statusIcon.set_visible(True)
    statusIcon.connect("popup-menu", popup_menu, menu)


    gtk.gdk.threads_init()
    gtk.main()
except (KeyboardInterrupt, SystemExit):
    updater.stop = True
    updater.stop_event.set()



