#
# GoodWeather Sensor
# Copyleft 2003 - Nifty hack by Konrad Rieck <kr@roqe.org>
# ---
# Desklet code by 		Martin Grimme  <martin@pycage.de>
# Weather.com backend by 	Matt <dmbkiwi@yahoo.com>
# Weather class by 		Heikki Lehvaslaiho <heikki@ebi.ac.uk>
# Icon sets by 			Peter Clark <pc451@yahoo.com> 
#     				Mattahan (http://Mattahan.deviantart.com)
#				Weather.com (http://www.weather.com)
# Data provided by  		Weather.com - Weather XML Data Feed
# Location patch by		Anatoly Asviyan <anatoly.asviyan@terayon.com>
# Bug fixes by			Jeremy Messenger <mezz7@cox.net> 
#                               Franz Klammer <klammer@webonaut.com> 
#                               Henry T. So Jr. <henryso@panix.com>
# Bug reports by		Jago <alex@tugab.net>
# ---
# This software is destributed under the terms of the GNU General Public
# License. This software is free software. You may distribute and modify
# this module freely
#  

from sensor.Sensor import Sensor
from utils.datatypes import *
from utils import i18n

import commands
import re
import os
import time
import Queue
import urllib
import weather

#
# Sensor for retrieving weather information.
#
class GoodWeatherSensor(Sensor):

    WEATHER_SOURCE = "http://xoap.weather.com/weather/local/" \
                     "%(weather_code)s?cc=*&dayf=5&prod=xoap&" \
                     "par=1003832479&key=bb12936706a2d601"

    CELSIUS = "celsius"
    FAHRENHEIT = "fahrenheit"
    SKY = 0
    HUMIDITY = 1
    WINDCHILL = 2
    PRESSURE = 3
    LOCATION = 4
    TIME = 5

    def __init__(self):

        global _; _ = i18n.Translator("weather-sensor")
        
        self.__queue = Queue.Queue(1)
        self.__path = os.getcwd()

        Sensor.__init__(self)

        self._set_config_type("weather_code", TYPE_STRING, "GMXX0007")
        self._set_config_type("update", TYPE_STRING, "30")
        self._set_config_type("icons", TYPE_STRING, "liquid")
        self._set_config_type("units", TYPE_STRING, self.CELSIUS)
        self._set_config_type("label1", TYPE_INT, self.SKY)
        self._set_config_type("label2", TYPE_INT, self.HUMIDITY)
        self._set_config_type("label3", TYPE_INT, self.WINDCHILL)

        self._add_thread(self.__get_weather_thread)
        self._add_timer(0, self.__get_weather)

    #
    # Returns the configuration GUI.
    #
    def get_configurator(self):

        configurator = self._new_configurator()
        configurator.set_name(_("GoodWeather"))

        configurator.add_title(_("Settings"))

        configurator.add_entry(_("Location Code:"), "weather_code", 
                               _("The weather.com location code, e.g. GMXX0007"))
        configurator.add_entry(_("Update Interval:"), "update", 
                               _("Interval to update display in minutes"))

        configurator.add_title(_("Measurement"))
        configurator.add_option(_("Measurement unit:"), "units",
                                _("Fahrenheit or Celsius"),
                                [(_("Celsius"), self.CELSIUS),
                                 (_("Fahrenheit"), self.FAHRENHEIT)])


        labels = [(_("Sky"), self.SKY),
                  (_("Humidity"), self.HUMIDITY), 
                  (_("Windchill Temp."), self.WINDCHILL), 
                  (_("Pressure"), self.PRESSURE), 
                  (_("Location"), self.LOCATION),         
                  (_("Update Time"), self.TIME)] 

        configurator.add_title(_("Display"))
        configurator.add_option(_("Label 1:"), "label1",
                                _("Choose the content of the label"),
                                labels)
        configurator.add_option(_("Label 2:"), "label2",
                                _("Choose the content of the label"),
                                labels)
        configurator.add_option(_("Label 3:"), "label3",
                                _("Choose the content of the label"),
                                labels)

        configurator.add_title(_("Theme"))
        configurator.add_option(_("Icons:"), "icons",
                                _("Choose an icon set"),
                                [(_("Liquid"), "liquid"),
                                 (_("UM"), "um"), 
                                 (_("weather.com"), "weather.com")])

        configurator.add_title(_("Weather XML Data Feed by weather.com"))
        
        return configurator

    #
    # Retrieves the weather data.
    #
    def __get_weather(self):

        # check the queue regularly
        if (not self.__queue.empty()):
            output = self.__queue.get()
            self._send_output(output)

        self._add_timer(500, self.__get_weather)

    #
    # Thread function for retrieving the weather data since this may take time
    # and must not block the application.
    #
    def __get_weather_thread(self):

        counter = 0
        old_values = ("", "", "", "", "", "", "")
        current_time = int(time.time())
        # Set us back update seconds so the if statment will be true
        update = int(self._get_config("update"))
        last_update = current_time - (update * 60) - 1

        icons = self._get_config("icons")
        output = self._new_output()
        output.set("temperature", "N/A")
        output.set("label1", "loading...")
        output.set("icon", os.path.join(self.__path, icons,
                   "large_icons", "36.png" % vars()))
        for i in range(0,5):
           output.set("icon"+str(i), os.path.join(self.__path, icons,
                      "small_icons", "36.png" % vars()))
        #end for                                                 
        self.__queue.put(output)
        
        while (1):
            if (self._is_stopped()): break

            weather_code = self._get_config("weather_code")
            units = self._get_config("units")
            icons = self._get_config("icons")
            update = int(self._get_config("update"))
            label1 = int(self._get_config("label1"))
            label2 = int(self._get_config("label2"))
            label3 = int(self._get_config("label3"))

            # Initalize the current time. 
            current_time = int(time.time())

            if ((weather_code, icons, units, update, label1, label2, label3 ) != old_values or \
                last_update + (update * 60) <= current_time):

                # At least 300/60 = 5 minutes this will run. Or so I
                # thought...  Sleep can take a few seconds to many minutes,
                # it's not accurate at all.  This is a problem for me, I'm
                # picky.  CN
                old_values = (weather_code, icons, units, update, label1, label2, label3)
                current_time = int(time.time())
                last_update = int(time.time())

		print 'Getting website...'
                fh = urllib.urlopen(self.WEATHER_SOURCE % vars())
                data = fh.read()
   
                data_error = re.search(r'''(<error>)''', data)
                if not data_error:
                   weather = parseGoodWeatherData(data) 
                else:
                   time.sleep(1)
                   continue

                metric = 0
                if (units == self.CELSIUS): 
                   metric = 1 
                #end if
                weather.metric(metric)

                output = self._new_output()

                try: 
                    output.set("temperature", weather.temperature())
                    output.set("sky", weather.sky())
                    output.set("rel_heat", weather.relative_heat())		 
                    output.set("rain", weather.rain())		 
                    output.set("humidity", weather.humidity())		 
                    output.set("pressure", weather.pressure())
                    output.set("time", weather.time())                    
                    output.set("place", weather.place())                    

                    # A switch statement would be really handy!
                    if label1 == self.HUMIDITY: 
                         output.set("label1", "H: "+weather.humidity())
                    elif label1 == self.WINDCHILL: 
                        output.set("label1", "W: "+weather.relative_heat())
                    elif label1 == self.LOCATION: 
                        output.set("label1", weather.place())
                    elif label1 == self.PRESSURE: 
                        output.set("label1", "P: "+weather.pressure())
                    elif label1 == self.TIME: 
                       output.set("label1", weather.time())
		    else:
                       output.set("label1", weather.sky())

                    if label2 == self.HUMIDITY: 
                         output.set("label2", "H: " + weather.humidity())
                    elif label2 == self.WINDCHILL: 
                        output.set("label2", "W: " + weather.relative_heat())
                    elif label2 == self.LOCATION: 
                        output.set("label2", weather.place())
                    elif label2 == self.PRESSURE: 
                        output.set("label2", "P: " + weather.pressure())
                    elif label2 == self.TIME: 
                       output.set("label2", weather.time())
		    else:
                       output.set("label2", weather.sky())

                    if label3 == self.HUMIDITY: 
                         output.set("label3", "H: " + weather.humidity())
                    elif label3 == self.WINDCHILL: 
                        output.set("label3", "W: " + weather.relative_heat())
                    elif label3 == self.LOCATION: 
                        output.set("label3", weather.place())
                    elif label3 == self.PRESSURE: 
                        output.set("label3", "P: " + weather.pressure())
                    elif label3 == self.TIME: 
                       output.set("label3", weather.time())
		    else:
                       output.set("label3", weather.sky())

                    icon_nr = weather.icon()
                    output.set("icon", os.path.join(self.__path, icons, 
                               "large_icons", "%(icon_nr)s.png" % vars()))
		    for i in range(0,5):
                       output.set("day"+str(i), 
                                  weather.get_forecast(i).weekday()[:3])
                       icon_nr = weather.get_forecast(i).icon()
                       output.set("icon"+str(i),os.path.join(self.__path,
                                  icons, "small_icons", 
                                  "%(icon_nr)s.png" % vars()))
		       output.set("temp"+str(i),
                                  weather.get_forecast(i).temp_range())
                    #end for 

                except:
                    output.set("temperature", "N/A")
                    output.set("sky", "N/A")
                    output.set("icon", os.path.join(self.__path, icons,
                               "large_icons", "36.png" % vars()))
                    for i in range(6):
                       output.set("icon"+str(i), os.path.join(self.__path,
                                  icons, "small_icons", "36.png" % vars()))
                    #end for                                                 
                self.__queue.put(output)
            #end if
            time.sleep(1)
        #end while

def parseGoodWeatherData(data):
	days = 5

	# Chop the data into more managable bits
	
	# The regular expressions needed to get the weather forecast
	# listed roughly in the order that they occur
	data = data.replace('CALM', 'Variable')
	data = data.replace('calm', '0')
	dcstart = data.find('<cc>')
	dcstop = data.find('</cc>')
	data_current = data[dcstart:dcstop]

	day0 = data.find('<day d="0"')
	day1 = data.find('<day d="1"')
	day2 = data.find('<day d="2"')
	day3 = data.find('<day d="3"')
	day4 = data.find('<day d="4"')
	data0 = data[day0:day1]
	data1 = data[day1:day2]
	data2 = data[day2:day3]
	data3 = data[day3:day4]
	data4 = data[day4:]

	p_city = data[data.find('<dnam>'):data.find('</dnam>')]
        p_city = p_city.replace('<dnam>', '')
        p_city = p_city[:p_city.find(',')]

	p_report = r'''(?ix)
	.*<lsup>(?P<month>\d+?)/ # Month
	(?P<day>\d+?)/ # Day
	(?P<year>\d+?) # Year
	\s(?P<time>.+?)\s #Time
	(?P<meridian>[A|P]M) # AM or PM
	\s(?P<zone>.*?)</lsup> # time zone
	\s*<obst>(?P<city>.*?), # City
	'''

	# time and location of the weather report
	now = weather.Observation()
	match = re.search(p_report, data_current)
	print match
	print match.group('city')
	if match:
		now.year(match.group('year'))
		now.month(match.group('month'))
		now.day(match.group('day'))
		now.time(match.group('time'), match.group('meridian'))
		now.timezone(match.group('zone'))
                #now.place(match.group('city'))
                now.place(p_city)

	p_weekday = r'''(?ix)
	<day\sd=\"0\"\st=\"(?P<weekday>.+?)\" # Day of the week
	'''

	match1 = re.search(p_weekday, data0)
	print match1
	print match1.group('weekday')
	if match1:
		now.weekday(match1.group('weekday')[:3])

	#print now
	print '==================================================='
	p_current = r'''(?isx) # Ignore case, Dot matches all, Verbose
	.*<tmp>(?P<temp>\d*?)</tmp> # Temp
	\s*<flik>(?P<heat>.*?)</flik> # Heat
	\s*<t>(?P<sky>\D+?)</t> # Sky
	\s*<icon>(?P<icon>\d*?)</icon> # Icon
	\s*<bar>\s*<r>(?P<baro>.*?)</r>\s*<d>(?P<change>.*?)</d>\s*</bar>
	\s*<wind>\s*<s>(?P<speed>\w*?)</s>.*<t>(?P<wind>.*?)</t>\s*</wind>
	\s*<hmid>(?P<hum>\d+)</hmid>
	\s*<vis>(?P<vis>.*?)</vis>
	\s*<uv>\s*<i>(?P<uv>.*?)</i>
	.*<dewp>(?P<dew>.*?)</dewp>
	'''

	match = re.search(p_current, data_current)
	print match
	print match.group('wind')
	if match:
		now.icon(match.group('icon'))
		now.temperature(match.group('temp'), 'F')
		now.relative_heat(match.group('heat'), 'F')
		now.sky(match.group('sky'))
		now.uv(match.group('uv'))
		now.dewpoint(match.group('dew'), 'F')
		now.humidity(match.group('hum'))
		now.visibility(match.group('vis'))
		now.pressure(match.group('baro'), 'inHg')
		now.pressure_change(match.group('change'))
		if match.group('wind') != 'CALM':
			mywind =  match.group('wind')
			gusting = mywind.find('gusting')
			if gusting != -1:
				jat = mywind.find(' at')
				mywind = mywind[:jat]
			now.wind(mywind.replace('From the ', ''))

		now.wind_speed(match.group('speed'), 'mph')
	print now

	p_today = r'''(?isx) # Ignore case, Dot matches all, Verbose
	<day\sd=\"0\".*dt=\"(?P<date>.*?)\"> # Date
	\s*?<hi>(?P<high>(\d*?|N/A))</hi> # High temperature
	\s*?<low>(?P<low>(\d*?|N/A))</low> # Low temperature
	.*?<icon>(?P<icon>\d*?)</icon> # Icon
	\s*?<t>(?P<sky>.*?)</t> # Sky
	.*?<ppcp>(?P<rain>\d+)</ppcp>  # Chance of rain
	'''

	today = weather.Forecast(now.weekday())
	now.add_forecast(today)
	match = re.search(p_today, data0)
	print match
	if match:
		today.icon(match.group('icon'))
		today.sky(match.group('sky'))
		today.date(match.group('date'))
		
                high = match.group('high')
		low = match.group('low')
                if high == 'N/A':
                    high = -100
                if low == 'N/A': 
                    low = -100
                if low > high:
                    high = -100
		
                today.temperature_high(high, 'F')
		today.temperature_low(low, 'F')

		today.rain(match.group('rain'));

	print "+++++++++++++++++++Today\'s Forecast++++++++++++++++++++++++"
	print today
	p_forecast = r'''(?isx) # No embedded flags, since they will be added later
	.*?t=\"(?P<day>.*?)\" # Weekday
	.*?dt=\"(?P<date>.*?)\"> # Day number
	\s*?<hi>(?P<high>(\d*?|N/A))</hi> # High temperature
	\s*?<low>(?P<low>(\d*?|N/A))</low> # Low temperature
	.*?<icon>(?P<icon>\d*?)</icon> # Icon
	\s*?<t>(?P<sky>.*?)</t> # Sky
	.*?<ppcp>(?P<rain>\d+)</ppcp>  # Chance of rain
	'''
	datalist = [data1, data2, data3, data4]
	day = 1
	while day <= (days -1):
		forecast = weather.Forecast()
		now.add_forecast(forecast)
		match = re.search(p_forecast, datalist[day-1])
		if match:
			forecast.weekday(match.group('day')[:3])
			forecast.date(match.group('date'))
			forecast.icon(match.group('icon'))
			forecast.sky(match.group('sky'))

                	high = match.group('high')
			low = match.group('low')
                	if high == 'N/A':
                    		high = -100
                	if low == 'N/A': 
                    		low = -100
                	if low > high:
                    		high = -100
		
                	forecast.temperature_high(high, 'F')
			forecast.temperature_low(low, 'F')

			forecast.rain(match.group('rain'));
		day += 1
		print forecast

	return now




def new_sensor(args): return apply(GoodWeatherSensor, args)

