#!/usr/bin/python
#program to control the sleep cycles of the XS
try:
    from gi.repository import Gtk, Gdk
except:
    import gtk as Gtk
from time import localtime, strftime
from datetime import date
from subprocess import Popen, PIPE
import datetime
import os, sys
import gconf
import logging
import json
from gettext import gettext as _
CONFIG_FILE = "/home/olpc/.sleepcfg"
SCRIPT_DIR = os.path.dirname(__file__)
WORK_DIR = "/home/olpc"

logger = logging.getLogger('serversleep')
hdlr = logging.FileHandler(os.path.join(WORK_DIR,'getsource.log'))
formatter = logging.Formatter('%(asctime)s %(levelname)s %(messages)s')
hdlr.setFormatter(formatter)
logger.addHandler(hdlr)
logger.setLevel(logging.WARNING)

#declare a global dictionary for config data
cfg = {}
builder = object()

class SleepTools:
    def parse_time(self, time_str):
        #return a tupple of int (hr,min) given input of string "hh:mm" form
        time_list = time_str.split(':')
        try:
            return (int(time_list[0]), int(time_list[1]))
        except:
            return (0,0)

    def calc_sleep(self):
        global cfg
        #return the hours from sleepstart to sleepend, considering skiped school days
        days = ["skipmon","skiptue","skipwed","skipthu","skipfri","skipsat","skipsun"]
        hrs, mins = self.parse_time(cfg["sleepstart"] )
        start = datetime.datetime.now()
        start = datetime.datetime(start.year, start.month, start.day, hrs, mins)
        cfg['sleepstartlong'] = self.tstamp(start)
        hrs, mins = self.parse_time(cfg["sleepstop"] )
        stop = datetime.datetime.now()
        stop = datetime.datetime(stop.year, stop.month, stop.day, hrs, mins)
        sleeptime = start- stop
        if sleeptime  > datetime.timedelta(0):
            sleeptime = datetime.timedelta(hours=24) - sleeptime
        daynum = datetime.datetime.now().weekday()
        for i in range (7):
            daynum += 1
            if daynum > 6:
                daynum = 0
            if not cfg[days[daynum]]:
                break
            sleeptime += datetime.timedelta(hours=24)

        stop = start + sleeptime
        cfg['sleepstoplong'] = self.tstamp(stop)
        #dt = datetime.datetime.fromtimestamp(cfg['sleepstartlong'])
        #print("begin sleeping at  %s. seconds since 1970:%s"% (dt.strftime("%d. %B %Y %I:%M%p"),
         #                                                      cfg['sleepstartlong'],))
        #dt = datetime.datetime.fromtimestamp(cfg['sleepstoplong'])
        #print("and wake up at  %s. seconds since 1970:%s"% (dt.strftime("%d. %B %Y %I:%M%p"),
         #                   cfg['sleepstoplong'],))
        return sleeptime


    def cli(self, cmd):
        """send cmd line to shell, rtn (text,error code)"""
        logger.debug('command_line cmd:%s'%cmd)
        p1 = Popen(cmd,stdout=PIPE, shell=True)
        output = p1.communicate()
        if p1.returncode != 0 :
            logger.debug('error returned from shell command: %s was %s'%(cmd,output[0]))
        return output[0],p1.returncode

    def tstamp(self, dtime):
        '''return a UNIX style timestamp for datetime input'''
        epoch = datetime.datetime(1970, 1, 1)
        since_epoch_delta = dtime - epoch
        return since_epoch_delta.total_seconds()

class PowerInterface(SleepTools):
	def __init__(self):
		global cfg
		global builder
		#get the config file with previous selections
		try:
			fd = file(CONFIG_FILE,'r')
		except IOError:
			#this is first run of program, set up defaults
			try:
				fd = file(CONFIG_FILE,'w')
				cfg = {
					"sleepenable" :False,
					"sleepstart"  :'17:00',
					"sleepstop"    :'6:00',
					"skipmon"     :False,
					"skiptue"     :False,
					"skipwed"     :False,
					"skipthu"     :False,
					"skipfri"     :False,
					"skipsat"     :True,
					"skipsun"     :True,
					"override"    :False,
					"sleepstoplong":0L,
					"sleepstartlong":0L
				}
				cfg_str = json.dumps(cfg)
				print ("json:%s."%(cfg_str,))
				fd.write(cfg_str)
				fd.close()
				fd = file(CONFIG_FILE,'r')
			except IOError,e:
				logging.exception("failed to write config str. error:%s"% (e,))
				fd = file(CONFIG_FILE,'r')
		cfg_str = fd.read()
		cfg = json.loads(cfg_str)

		#use Glade generated XML description of UI GTK widgets to create window
		builder = Gtk.Builder()
		fname = os.path.join(SCRIPT_DIR,"sleep.glade")
		builder.add_from_file(fname)
		window = builder.get_object("window1")
		window.show_all()
		handlers = {
			"onDeleteWindow": self.done,
			"override_clicked_cb"   : self.override,
			"enable_toggled_cb"     : self.enable_toggled,
			"sleepbegin_changed_cb" : self.sleepstart_changed,
			"sleepend_changed_cb"  : self.sleepstop_changed,
			"mon1_toggled_cb"       : self.mon1_toggled,
			"tue1_toggled_cb"       : self.tue1_toggled,
			"wed1_toggled_cb"       : self.wed1_toggled,
			"thu1_toggled_cb"       : self.thu1_toggled,
			"fri1_toggled_cb"       : self.fri1_toggled,
			"sat1_toggled_cb"       : self.sat1_toggled,
			"sun1_toggled_cb"       : self.sun1_toggled,
			"newtime_activate_cb"	: self.time_changed_cb,
		}

        builder.connect_signals(handlers)

        #create hourlist for combo boxes
        self.hrlist = []
        for hr in range(23):
            self.hrlist.append("%s:00"%hr)
            #hrlist.append("%s:30"%hr)
        time_model = Gtk.ListStore(str)
        for tm in self.hrlist:
            time_model.append([tm])

        enable_checkbox = builder.get_object("sleepenable")
        enable_checkbox.set_active(cfg["sleepenable"])

        start_school = builder.get_object("sleepbegin")
        start_school.set_model(time_model)
        renderer = Gtk.CellRendererText()
        start_school.pack_start(renderer, True)
        start_school.add_attribute(renderer,"text",0)
        start_school.set_active(self.getindex(self.hrlist, cfg['sleepstart']))

        stop_school = builder.get_object("sleepend")
        stop_school.set_model(time_model)
        renderer = Gtk.CellRendererText()
        stop_school.pack_start(renderer, True)
        stop_school.add_attribute(renderer,"text",0)
        stop_school.set_active(self.getindex(self.hrlist, cfg['sleepstop']))

        mon1_check_box = builder.get_object("mon1")
        mon1_check_box.set_active(cfg["skipmon"])

        tue1_check_box = builder.get_object("tue1")
        tue1_check_box.set_active(cfg["skiptue"])

        wed1_check_box = builder.get_object("wed1")
        wed1_check_box.set_active(cfg["skipwed"])

        thu1_check_box = builder.get_object("thu1")
        thu1_check_box.set_active(cfg["skipthu"])

        fri1_check_box = builder.get_object("fri1")
        fri1_check_box.set_active(cfg["skipfri"])

        sat1_check_box = builder.get_object("sat1")
        sat1_check_box.set_active(cfg["skipsat"])

        sun1_check_box = builder.get_object("sun1")
        sun1_check_box.set_active(cfg["skipsun"])

        label = builder.get_object("currenttimezone")
        client = gconf.client_get_default()
        #timezone = client.get_string('/desktop/sugar/date/timezone')
        timezone = os.environ.get('TZ', "UTC")
        os.environ["TZ"] = timezone
        label.set_text("TimeZone: %s  Current Time: %s"%\
			(timezone, strftime("%H:%M",localtime())))

    def getindex(self, lst, val):
        #return index of val in list or -1 if not in list
        try:
            return(lst.index(val))
        except ValueError:
            return(-1)

    def show_override_status(self):
        over_button  = builder.get_object("override")
        if not cfg["sleepenable"]:
            over_button.set_label(_("Status: Server Sleep is disabled"))
        elif cfg["override"]:
            over_button.set_label(_("Status: Next sleep cycle disabled\015\012Click here to re-enable."))
        else:
            sleeptime = self.calc_sleep()
            over_button.set_label(_("Status: Server will sleep at %s for %s hours.\015\012 Click here to disable one sleep cycle")%\
            (cfg["sleepstart"], sleeptime.total_seconds() // 3600))


    def enable_toggled(self, widget):
        global cfg
        cfg["sleepenable"] = widget.get_active()
        if cfg["sleepenable"]:
            cfg["override"] = False
        self.save_cfg()
        self.show_override_status()


    def sleepstart_changed(self, widget):
        global cfg
        index = widget.get_active()
        cfg["sleepstart"] = self.hrlist[index]
        self.save_cfg()
        self.show_override_status()

    def sleepstop_changed(self, widget):
        global cfg
        index = widget.get_active()
        cfg["sleepstop"] = self.hrlist[index]
        self.save_cfg()
        self.show_override_status()

    def mon1_toggled(self, widget):
        global cfg
        cfg["skipmon"] = widget.get_active()
        self.save_cfg()
        if self.check_seven_days("skipmon", widget): return self.complain_seven_days()
        self.show_override_status()

    def tue1_toggled(self, widget):
        global cfg
        cfg["skiptue"] = widget.get_active()
        self.save_cfg()
        if self.check_seven_days("skipmon", widget): return self.complain_seven_days()
        self.show_override_status()

    def wed1_toggled(self, widget):
        global cfg
        cfg["skipwed"] = widget.get_active()
        self.save_cfg()
        if self.check_seven_days("skipmon", widget): return self.complain_seven_days()
        self.show_override_status()

    def thu1_toggled(self, widget):
        global cfg
        cfg["skipthu"] = widget.get_active()
        self.save_cfg()
        if self.check_seven_days("skipmon", widget): return self.complain_seven_days()
        self.show_override_status()

    def fri1_toggled(self, widget):
        global cfg
        cfg["skipfri"] = widget.get_active()
        self.save_cfg()
        if self.check_seven_days("skipmon", widget): return self.complain_seven_days()
        self.show_override_status()

    def sat1_toggled(self, widget):
        global cfg
        cfg["skipsat"] = widget.get_active()
        self.save_cfg()
        if self.check_seven_days("skipmon", widget): return self.complain_seven_days()
        self.show_override_status()

    def sun1_toggled(self, widget):
        global cfg
        cfg["skipsun"] = widget.get_active()
        self.save_cfg()
        if self.check_seven_days("skipmon", widget): return self.complain_seven_days()
        self.show_override_status()

    def time_changed_cb(self,widget):
        label = builder.get_object("currenttimezone")
        time = widget.get_text()
        label.set_text(time)

    def override(self, widget):
        global cfg
        if  not cfg["sleepenable"]:
            #maybe I should popup an error explanation here
            self.show_override_status()
            return
        cfg["override"] = not cfg["override"]
        if cfg["override"]:
            self.set_color(widget, Gdk.Color.red)
        else:
            self.set_color(widget, Gdk.Color.green)
            #if currently in a sleep period, sleep now
            if datetime.now() > datetime.fromtimestamp(cfg['sleepstartlong']) & \
                datetime.now() < datetime.fromtimestamp(cfg['sleepstoplong']):
                over_button.set_label(_("Status: Server will sleep withiin the next hour"))
            self.save_cfg()
        self.show_override_status()


    def done(self, widget, data):
        Gtk.main_quit()

    def save_cfg(self):
        global cfg
        try:
            fd = file(CONFIG_FILE,'w')
            cfg_str = json.dumps(cfg)
            fd.write(cfg_str)
            fd.close()
        except IOError,e:
            logging.exception("failed to write config str. error:%s"% (e,))

    def set_color(self, obj, color):
        #make a gdk.color
        #map = obj.get_colormap()
        #color = map.alloc_color(Gdk.color_parse(color_str))
        #parse, color = Gdk.Color.parse(color_str)
        #copy the current style and replace the background
        style = obj.get_style().copy()
        style.bg[Gtk.StateType.NORMAL] = Gdk.Color.green
        #obj.override_background_color(Gtk.StateFlags.NORMAL, Gdk.RGBA(1, 0, 0, 1))

        #set the button's style to the one you created
        obj.set_style(style)

    def check_seven_days(self, day_str, widget):
        #check to see if this is the seventh day, if so return true
        days = ["skipmon","skiptue","skipwed","skipthu","skipfri","skipsat","skipsun"]
        daynum = current_day = days.index(day_str)
        disabled = 0
        for i in range(7):
            daynum += 1
            if daynum > 6:
                daynum = 0
            if cfg[days[daynum]]:
                disabled += 1
            if daynum == current_day and disabled == 7:
                widget.set_active(False)
                cfg[day_str] = False
                self.save_cfg()
                return True
        return False

    def complain_seven_days(self):
        over_button  = builder.get_object("override")
        over_button.set_label(_("You cannot disable all days in the week"))


class PowerAction(SleepTools):
    def __init__(self):
        #get the config file with previous selections
        try:
            fd = file(CONFIG_FILE,'r')
            cfg_str = fd.read()
        except Exception,e:
            logger.exception("cron invocation failed to read cfg. e=%s"%e)
            return
        cfg = json.loads(cfg_str)

        #is current time > sleepstart?
        daynum = datetime.datetime.now().weekday()
        hrs, mins = self.parse_time(cfg["sleepstart"] )
        current  = datetime.datetime.now()
        start = datetime.datetime(current.year, current.month, current.day, hrs, mins)
        if current > cfg["sleepstartlong"]:
            #are we in an override situation?
            if cfg["override"]:
                if current > cfg["sleepstoplong"]:
                    # end of sleep period, turn off override
                    cfg["override"] = False
                return
            # set stop/wakeup time
            self.sleep()

    def sleep():
        sleeptime = cfg["sleepstoplong"] - datetime.datetime.now()
        rtcstring = "rtcwake -s %s -m disk"%(sleeptime,)
        p1 = Popen(rtcstring,stdout=PIPE, shell=True)

if __name__ == "__main__":
    # if interactive from command line, do gui
    if len(sys.argv) == 1:
        pi = PowerInterface()
    elif len(sys.argv == 2):
        # if coming from cron, the check for an action to do
        if sys.argv[1] == '--timeout':
            pa = PowerAction()
        sys.exit(0)
    # pop up the GUI
    Gtk.main()
    exit(0)
