#!/usr/bin/env python
# -*- coding: UTF-8 -*-

# (C) Eric Herrera <eric.gnome@gmail.com>
# Licensed under the GNU GPL.  

import os
import re
import sys
import time
import gobject

try:
    import gtk
    import gst
    import pynotify
    import gnome.ui
    import gnomeapplet
except ImportError, e:
    print e
    sys.exit(1)    
    
from festival import Festival
from preferences_window import Preferences

class PythonApplet(gnomeapplet.Applet):

    def __init__ (self, applet, iid):    	

        self.applet = applet

    	self.ebox = gtk.EventBox()	
    	self.ebox.connect("button_press_event", self.on_applet_button_press)
    	self.applet.add(self.ebox)        

        self.img = gtk.Image()
        self.img.set_from_file(os.path.join("/usr/share/AlarmClockApp", "clock.png"))
        self.ebox.add(self.img)

        self.tooltips = gtk.Tooltips()
        self.tooltips.set_tip(self.img, "Disable alarm")

        self.applet.connect("change_background", self.change_background)
        self.applet.connect("destroy", self.on_destroy)
    	self.applet.show_all()    	 

        self.make_main_window()
        self.child_window = FestWinPrefs(parent=self)
        self.create_playbin()
        try:
            self.festival = Festival()
        except:
            self.check3.set_sensitive(False)
            print "Alarm voice option require festival package."

        self.p0 = re.compile("(\d+)\D+(\d+)\D+(\d+)")
        self.p1 = re.compile("(\d+)\D+(\d+)")
        self.home_dir = os.getenv("HOME")
        self.enable_alarm = False
        self.enable_voice = False
        self.is_count  = False
        self.custom_message = False
        self.file = None
        self.spin_mins = 0
        self.count = 0
       
        self.verbs = [ ("Alarm", self.menu_alarm_toggled),
                       ("Prefs", self.festwin_preferences), 
                       ("About", self.about_info) ]

        self.timeout_add()

    def change_background (self, panelapplet, backgroundtype, color, pixmap, *args):
        self.applet.modify_bg(gtk.STATE_NORMAL, color)
        self.ebox.modify_bg(gtk.STATE_NORMAL, color)

        if pixmap is not None:
            s1 = self.applet.get_style()
            s2 = self.ebox.get_style()
            s1.bg_pixmap[gtk.STATE_NORMAL] = pixmap
            s2.bg_pixmap[gtk.STATE_NORMAL] = pixmap
            self.applet.set_style(s1)
            self.ebox.set_style(s2)
            
    def set_menu (self):
        # Si la alarma es False esta desactivada y muestra el menu para activarla.
        if not self.enable_alarm:
            self.applet.setup_menu_from_file("/usr/share/AlarmClockApp", "alarmclockapp_menu1.xml", None, self.verbs)
        else:
            self.applet.setup_menu_from_file("/usr/share/AlarmClockApp", "alarmclockapp_menu2.xml", None, self.verbs)

    def on_applet_button_press (self, widget, event):
        if event.button == 1:
            self.showHide(self.ebox)

    	elif event.button == 3:
            self.set_menu()

    def menu_alarm_toggled (self, event, data=None):
        if not self.enable_alarm:
            self.check_alarm_toggled.set_active(True)
            self.tooltips.set_tip(self.img, "Enable alarm")
        else:
            self.check_alarm_toggled.set_active(False)
            self.tooltips.set_tip(self.img, "Disable alarm")
            self.check2.set_active(False)
            gst.STATE_NULL
    
    def festwin_preferences(self, event, data=None):
        self.child_window.win.show_all()
    
    def about_info (self, event, data=None):
        dialog = gnome.ui.About("AlarmClockApp", "0.1.3", "A simple alarm clock that uses pygtk, GStreamer and festival for speach text", "GPL Licence", ["Eric Herrera <eric.gnome@gmail.com>"])
        dialog.show()

    def make_main_window(self):
        self.main_win = gtk.Window()
        self.main_win.set_border_width(7)
        self.main_win.set_title("Set alarm")
        self.main_win.set_property("resizable", False)
        self.main_win.set_destroy_with_parent(True)

        self.main_win.connect("delete_event", self.on_win_delete_event)
        self.main_win.set_position(gtk.WIN_POS_MOUSE)
        self.main_win.set_default_size(140, 240)

        # main row 1
        vbox_main = gtk.VBox(False, 3)
        frame = gtk.Frame("Set alarm clock")
        vbox_main.pack_start(frame, True, True, 3)

        # vbox in frame        
        vbox = gtk.VBox(False, 0)
        frame.add(vbox)
        vbox.set_border_width(7)
        
        # row 1
        label1 = gtk.Label("Current time: ")
        self.time_label = gtk.Label()

        hbox = gtk.HBox(False, 0)
        hbox.pack_start(label1, False, False, 3)
        hbox.pack_start(self.time_label, False, False, 3)
        vbox.pack_start(hbox, False, False, 5)

        #row 2
        hbox2 = gtk.HBox(False, 0)
        self.check_alarm_toggled = gtk.CheckButton("_Enable alarm at  ")
        self.check_alarm_toggled.connect("toggled", self.on_toggled_alarm_activate)
        self.alarm_label = gtk.Label()
        self.cbh_entry = '00'
        self.cbm_entry = '00'
        self.alarm_label.set_text("%s:%s" % (self.cbh_entry, self.cbm_entry))
        self.alarm_label.set_sensitive(False)
                
        hbox2.pack_start(self.check_alarm_toggled, False, False, 0)
        hbox2.pack_start(self.alarm_label, False, False, 0)
        vbox.pack_start(hbox2, False, False, 5)

        # row 3
        hbox3 = gtk.HBox(False, 2)
        self.label2 = gtk.Label("hour:")
        self.combo_hrs = gtk.combo_box_entry_new_text()
        
        for i in range(0, 10):
            self.combo_hrs.append_text("0%i" % i)
        for hrs in range(10, 25):
            self.combo_hrs.append_text( str(hrs) )
        self.combo_hrs.child.connect("changed", self.on_cbh_changed)
        self.combo_hrs.set_active(0)
        self.combo_hrs.set_size_request(60, 25)
        self.combo_hrs.set_wrap_width(5)

        self.label4 = gtk.Label("mins:")
        self.combo_min = gtk.combo_box_entry_new_text()
        for i in range(0, 10, 5):
            self.combo_min.append_text( str("0%i" % i) )
        for mins in range(10, 60, 5):
            self.combo_min.append_text( str(mins) )
        self.combo_min.child.connect("changed", self.on_cbm_changed)
        self.combo_min.set_active(0)
        self.combo_min.set_size_request(60, 25)
        self.combo_min.set_wrap_width(4)
        self.widgets = (self.label2, self.combo_hrs, self.label4, self.combo_min)
        for elem in self.widgets:        
            hbox3.pack_start(elem, False, False, 0)
            elem.set_sensitive(False)
        vbox.pack_start(hbox3, False, False, 6)

        # row 5
        hbox5 = gtk.HBox(False, 0)
        self.label4 = gtk.Label()
        self.label4.set_markup("<i>Pick an alarm sound:</i>")
        hbox5.pack_start(self.label4, False, False, 0)
        vbox.pack_start(hbox5, False, False, 7)

        # row 6
        hbox6 = gtk.HBox(False, 0)
        self.fc = gtk.FileChooserButton("Select a File", None)
        self.fc.set_action(gtk.FILE_CHOOSER_ACTION_OPEN)
        file_filter = gtk.FileFilter()
        file_filter.set_name("Audio Files")
        file_filter.add_mime_type("audio/*")
        file_formats = ("*.ogg", "*.mp3", "*.wma", "*.wav", "*.flac", "*.mp4")
        for formats in file_formats:
            file_filter.add_pattern(formats)
        self.fc.add_filter(file_filter)
        self.default_file = os.path.join("/usr/share/AlarmClockApp", "sound1.wav")
        self.fc.set_filename(self.default_file)
        
        hbox6.add(self.fc)
        vbox.pack_start(hbox6, False, False, 2)
        
        self.fc.set_flags(gtk.CAN_FOCUS)
        # permite adquirir el foco con la bandera activada
        self.fc.grab_focus()
        self.fc.set_flags(gtk.HAS_DEFAULT)

        # row 7
        hbox7 = gtk.HBox(False, 0)
        self.check = gtk.CheckButton("_Loop sound")
        self.check.connect("toggled", self.on_check_toggled_loop)
        hbox7.pack_start(self.check, False, False, 0)
        vbox.pack_start(hbox7, False, False, 5)

        #row 8
        hbox9 = gtk.HBox(False, 0)
        self.check3 = gtk.CheckButton("Alarm _voice")
        self.check3.connect("toggled", self.on_check_enable_voice)
        hbox9.pack_start(self.check3, False, False, 0)
        vbox.pack_start(hbox9, False, False, 5)


        # row 9
        hbox8 = gtk.HBox(False, 0)        
        self.check2 = gtk.CheckButton("Enable alarm _repeat") 
        self.check2.grab_focus()    
        self.check2.connect("toggled", self.on_alarm_repeat_toggled)   

        self.label7 = gtk.Label("Sound every:")
        self.label7.set_sensitive(False)

        self.label8 = gtk.Label("mins")
        self.label8.set_sensitive(False)

        adj = gtk.Adjustment(value=0, lower=0, upper=60, step_incr=1, page_incr=1, page_size=1)
        self.spin1 = gtk.SpinButton(adj, 0, 0)
        self.spin1.set_sensitive(False)
        self.spin1.set_numeric(True)
        self.spin1.set_size_request(18, 23)
        self.spin1.connect("value-changed", self.on_spin_value_changed)

        hbox8.pack_start(self.label7, False, False, 10)
        hbox8.add(self.spin1)
        hbox8.add(self.label8)

        vbox.pack_start(self.check2, False, False, 4)
        vbox.pack_start(hbox8, False, False, 3)

        # main row 2
        close_button = gtk.Button(stock=gtk.STOCK_CLOSE)
        close_button.connect("clicked", self.on_close)
        
        self.stop_button = gtk.Button(None, gtk.STOCK_MEDIA_STOP)
        self.stop_button.connect("clicked", self.on_stop)

        hbbox = gtk.HButtonBox()
        hbbox.set_layout(gtk.BUTTONBOX_EDGE)
        hbbox.add(self.stop_button)
        hbbox.add(close_button)
        hbbox.set_spacing(5)
        vbox_main.pack_start(hbbox, False, False, 0)

        self.main_win.add(vbox_main)
        
    def on_toggled_alarm_activate(self, check_alarm_activate):
        if check_alarm_activate.get_active():
            self.enable_alarm = True
            self.alarm_label.set_sensitive(True)
            self.tooltips.set_tip(self.img, "Enable alarm")
            for widgets in self.widgets:
                widgets.set_sensitive(True)
        else:
            self.enable_alarm = False
            self.check2.set_active(False)
            gst.STATE_NULL
            self.alarm_label.set_sensitive(False)
            self.tooltips.set_tip(self.img, "Disable alarm")
            for widgets in self.widgets:
                widgets.set_sensitive(False)
            
    def on_cbh_changed(self, entry):
        self.cbh_entry = entry.get_text()
        try:
            self.alarm_label.set_text("%s:%s" % (self.cbh_entry, self.cbm_entry) )
            # Reiniciar el contador
            self.is_cout = False
            self.count = 1
        except:
            # La excepcion no es tratada solo saltada.
            pass

    def on_cbm_changed(self, entry):
        self.cbm_entry = entry.get_text()
        try:
            self.alarm_label.set_text("%s:%s" % (self.cbh_entry, self.cbm_entry) )
            # Reiniciar el contador
            self.is_count = False
            self.count = 1
        except:
            pass

    def on_spin_value_changed(self, spin):        
        spin_value = spin.get_value_as_int()
        self.spin_mins = spin_value * 60
        if self.spin_mins == 0:
            self.count = 1
      
    def timeout_add(self):
        self.timer = gobject.timeout_add(1000, self.timeout_add) 
        self.time_label.set_markup('<b>%s</b>' % time.strftime("%H:%M:%S") )
        self.comp_labels()

        if self.is_count:
            self.count +=1

        if self.count == self.spin_mins:
            self.count = 0
            
            if self.enable_alarm:
                if self.enable_voice == False:
                    self.play_sound()
                    self.make_pynotify("Wake Up!", "Alarm at %s:%s" % (self.h, self.m), "continue dreaming")
                else:
                    self.make_pynotify("Wake Up!", "Alarm at %s:%s" % (self.h, self.m), "continue dreaming")
                    if self.custom_message:
                        self.festival.say_text(self.child_window.entry.get_text())                                    
                    else:
                        self.festival.say_text(time.strftime("%H:%M:%S"))                
            else:
                self.player.set_state(gst.STATE_READY)

        if self.spin_mins == 0:
            self.count = 1
            self.is_count = False # bandera que inicia el contador.
            
        print self.count            
         
    def comp_labels(self):
        search = self.p0.search( self.time_label.get_text() )
        h = int( search.group(1) )
        m = int( search.group(2) )
        s = int( search.group(3) )
        try:
            search2 = self.p1.search( self.alarm_label.get_text() )
            self.h = int( search2.group(1) )
            self.m = int( search2.group(2) )
            self.s = 0
        except:
            self.h = 0
            self.m = 0
        
        if (self.h == h and self.m == m and self.s == s):
            
            if not self.enable_alarm:
                self.player.set_state(gst.STATE_NULL)
            else:
                if self.enable_voice:
                    self.is_count = True
                    self.make_pynotify("Wake Up!", "Alarm at %s:%s" % (self.h, self.m), "continue dreaming")
                    if self.custom_message:
                        self.festival.say_text(self.child_window.entry.get_text())
                    else:
                       self.festival.say_text(time.strftime("Time now %H:%M:%S, wake up. lazy"))
                else:
                    self.play_sound()
                    self.is_count = True # bandera para el contador       
                    self.make_pynotify("Wake Up!", "Alarm at %s:%s" % (self.h, self.m), "continue dreaming")
                
    def play_sound(self):
        self.file = self.fc.get_filename()
        if self.file:
            is_file = os.path.isfile(self.file)
            if is_file:
                self.player.set_state(gst.STATE_READY)
                self.player.set_property("uri", "file://%s" % self.file)
                self.player.set_state(gst.STATE_PLAYING)
            else:
                self.player.set_state(gst.STATE_NULL)
        else:
            self.fc.set_filename(self.default_file)
            self.player.set_state(gst.STATE_READY)
            self.player.set_property("uri", "file://%s" % self.default_file)
            self.player.set_state(gst.STATE_PLAYING)

    def on_stop(self, widget): 
        self.player.set_state(gst.STATE_READY)
        self.n.close()
            
    def on_check_enable_voice(self, check_btn):
        widgets = (self.fc, self.label4, self.check)            
        if check_btn.get_active():
            self.enable_voice = True
            for widget in widgets:
                widget.set_sensitive(False)
        else:
            self.enable_voice = False
            for widget in widgets:
                widget.set_sensitive(True)

    def on_alarm_repeat_toggled(self, check_btn):
        widgets = (self.spin1, self.label7, self.label8)
        if not check_btn.get_active():
            self.spin1.set_value(0)
            for w in widgets:
                w.set_sensitive(False)
        else:
            for w in widgets:
                w.set_sensitive(True)
                
    def create_playbin(self):
        self.player = gst.element_factory_make("playbin")
        bus = self.player.get_bus()
        bus.add_signal_watch()
        bus.connect("message", self.on_message_bus)
        self.player.set_state(gst.STATE_READY)

    # El método se llama en EOS
    def on_check_toggled_loop(self, *args):
        if not self.check.get_active():
            return False
        else:
            return True

    def on_message_bus(self, bus, message):
        t = message.type
        if t == gst.MESSAGE_EOS:
            if self.on_check_toggled_loop():
                time.sleep(1.5)
                if self.enable_alarm:
                    self.play_sound()
                else:
                    gst.STATE_NULL
                
        elif t == gst.MESSAGE_ERROR:
            gst.STATE_NULL

    def make_pynotify(self, title, message, btn_text):
        self.n = pynotify.Notification(title, message, gtk.STOCK_DIALOG_WARNING)
        pynotify.init("notificacion")
        self.n.set_urgency(pynotify.URGENCY_NORMAL)
        self.n.set_timeout(8500)
        self.n.set_category("device")
        self.n.add_action("clicked", btn_text, self.on_pynotify_clicked)
        self.n.show()

    def on_pynotify_clicked(self, n, action):
        self.player.set_state(gst.STATE_NULL)
        n.close()

    def on_win_delete_event(self, widget, event):
        widget.hide()
        return True

    def on_close(self, widget):
        self.main_win.hide()

    def on_destroy (self, event):
        del self.applet
        
    def showHide(self, widget=None):
        if self.main_win.flags() & gtk.VISIBLE:
            self.main_win.hide()
        else:
            self.main_win.deiconify()
            self.main_win.show_all()      
  
class FestWinPrefs(Preferences):

    def new(self):
        self.radio_0.connect("toggled", self.on_radio_toggled)
        self.check.connect("toggled", self.on_custom_message)
               
    def on_radio_toggled(self, widget):
        if self.radio_0.get_active():
            self.parent.festival.set_voice("kal")                           

        if self.radio_1.get_active():
            self.parent.festival.set_voice("el")

    def on_custom_message(self, check):
        if check.get_active():
            self.entry.set_sensitive(True)
            self.entry.set_text("")
            self.parent.custom_message = True
        else:
            self.entry.set_sensitive(False)
            self.parent.custom_message = False        
                

def sample_factory(applet, iid):
    PythonApplet(applet, iid)
    return True

if len(sys.argv) > 1 and sys.argv[1] == "run-in-window":
    main_window = gtk.Window(gtk.WINDOW_TOPLEVEL)
    main_window.set_title("AlarmClockApp Applet")
    main_window.set_default_size(50, 50)
    main_window.connect("destroy", lambda w: gtk.main_quit())
    app = gnomeapplet.Applet()
    sample_factory(app, None)
    app.reparent(main_window)
    main_window.show_all()
    gtk.main()
    sys.exit()

if __name__ == "__main__":  
    gnomeapplet.bonobo_factory("OAFIID:Pyapplet_Factory",		    
                    PythonApplet.__gtype__,
				    "helloapplet", "0", 
                    sample_factory)
