#!/usr/bin/env python
# -*- coding: us-ascii -*-

import wx
import time
import math
DEBUG=1

class AlarmTimer(wx.Frame):
    """
    The __alarmTime Class is a Frame containing a simple timer that blinks and
    outputs a sound when a specified time is up.
    """
    def __init__(self, inParent, inId, inTitle, inAlarmTime=10, inAlarmDuration=10, inAlarmStaysOn=True, inAlarmSound=None):
        wx.Frame.__init__(self, inParent, inId, inTitle, style=wx.DEFAULT_FRAME_STYLE)
        
        ##### Hidden PROPERTIES
        self.__displayUpdateTimerInterval = 500
        self.__alarmUpdateTimerInterval = 1000
        self.__startTime = None
        self.__alarmTrippedTime = None
        self.__alarmTripped = False
        self.__labelNormalForeground = wx.Color(0,0,0)
        self.__labelAlarmForeground = wx.Color(255,0,0)
        self.__labelFont = wx.Font(40, wx.SCRIPT, wx.NORMAL, wx.BOLD, 0, "")
        
        ##### Objects
        self.__alarmTimerLabel = wx.StaticText(self, -1, "00:00", style=wx.ALIGN_CENTRE)
        self.__displayUpdateTimer = wx.Timer(self,-1)
        self.__alarmUpdateTimer = wx.Timer(self,-1)
        
        ##### Object PROPERTIES
        self._setAlarmTime = None; self.setAlarmTime(inAlarmTime)
        self._setAlarmDuration = None; self.setAlarmDuration(inAlarmDuration)
        self._setAlarmStaysOn = None; self.setAlarmStaysOn(inAlarmStaysOn)
        self._setAlarmSound = None; self.setAlarmSound(inAlarmSound)
            
        ##### Event Bindings
        self.Bind(wx.EVT_TIMER, self.__onDisplayUpdateTimer, self.__displayUpdateTimer)
        self.Bind(wx.EVT_TIMER, self.__onAlarmUpdateTimer, self.__alarmUpdateTimer)
        self.__alarmTimerLabel.Bind(wx.EVT_LEFT_DCLICK, self.__onLeftDClick)

        ##### Tidying up
        self.SetForegroundColour(self.__labelNormalForeground)
        self.__alarmTimerLabel.SetFont(self.__labelFont)
        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(self.__alarmTimerLabel, 1, wx.ALL | wx.EXPAND, 0)
        self.SetSizer(sizer)
        sizer.Fit(self)
        self.Layout()

    def getAlarmTime(self):
        return self._alarmTime

    def getAlarmDuration(self):
        return self._alarmDuration

    def isAlarmStaysOn(self):
        return self._alarmStaysOn

    def getAlarmSound(self):
        return self._alarmSound

    def setAlarmTime(self, value):
        """
        set the time after which the alarm is tripped
        value is a floating point value >= 0 in seconds
        set to 0 for no alarm
        """
        if (value<0):
            raise ValueError("Alarm time must be >= 0");
        else:
            self._alarmTime = float(value)
            
    def setAlarmTimeInMins(self, value):
        """
        set the time after which the alarm is tripped
        value is a floating point value >= 0 in minutes
        set to 0 for no alarm
        """
        self.setAlarmTime(value*60)

    def setAlarmDuration(self, value):
        """
        set the duration for which the alarm is going off after being tripped
        value is a floating point value >= 0 in seconds
        set to 0 for endless alarm
        """
        if (value<0):
            raise ValueError("Alarm time must be >= 0");
        else:
            self._alarmDuration = float(value)
        
    def setAlarmStaysOn(self, value):
        """
        defines if the alarm stays tripped after reaching the end of its
        duration
        """
        self._alarmStaysOn = value

    def setAlarmSound(self, value):
        """
        defines the sound byte that plays when the alarm is tripped
        value is a wxSound object 
        """
        self._alarmSound = value
        try:
            assert value.IsOk()
        except:
            print "WARNING: sound did not load properly, no sound will be produced"
            self._alarmSound = None
    
    def setAlarmSoundFile(self, value):
        """
        defines the sound byte that plays when the alarm is tripped
        value is a file name
        """
        sound = wx.Sound(value)
        self.setAlarmSound(sound)
    
    alarmTime = property(getAlarmTime,setAlarmTime,None,"")
    alarmDuration = property(getAlarmDuration,setAlarmDuration,None,"")
    alarmStaysOn = property(isAlarmStaysOn,setAlarmStaysOn,None,"")
    alarmSound = property(getAlarmSound,setAlarmSound,None,"")
        
  
    def __onDisplayUpdateTimer(self, event):
        """
        Event handler of the __displayUpdateTimer Timer
        
        This function updates the label with the amount of mm:ss since the start
        of the time, and detects if the timer reached the alarm point and trips
        it 
        """
        #if DEBUG: print "got event %s at time %s"%(event, time.ctime())
        timeInSecsSinceStart = time.time()-self.__startTime
        minutes = math.floor(timeInSecsSinceStart/60 % 60)
        secs = timeInSecsSinceStart - minutes*60
        label = "%02d:%02d"%(minutes,secs);
        #if DEBUG: print "Current Timer Value is %.2fs (%.2fm:%.2fs)"%(timeInSecsSinceStart,minutes,secs)
        if (self.__alarmTimerLabel.GetLabelText()<>label):
            self.__alarmTimerLabel.SetLabel(label)
            
        if self._alarmTime>0 and (time.time()-self.__startTime)>self._alarmTime and not self.__alarmTripped:
            if DEBUG: print "Alarm tripped at time %s"%time.ctime()
            self.__alarmTrippedTime = time.time()
            self.__alarmTripped = True
            self.__alarmUpdateTimer.Start(self.__alarmUpdateTimerInterval)
            
    def __onAlarmUpdateTimer(self, event):
        """
        Event handler for the __alarmUpdateTimer Timer
        
        This function blinks the display and plays the soundbyte during the
        alarm 
        """
        #if DEBUG: print "in onAlarmUpdateTime at time %s"%time.ctime()
        if self.__alarmTimerLabel.GetForegroundColour()==self.__labelNormalForeground:
            #if DEBUG: print "switching to alarm color"
            self.__alarmTimerLabel.SetForegroundColour(self.__labelAlarmForeground)
            if (self._alarmSound is not None) and self._alarmSound.IsOk():
                self._alarmSound.Play(wx.SOUND_ASYNC)
        else:
            #if DEBUG: print "switching to normal color"
            self.__alarmTimerLabel.SetForegroundColour(self.__labelNormalForeground)
        if (self._alarmDuration>0) and (time.time()-self.__alarmTrippedTime>self._alarmDuration):
            if self._alarmStaysOn:
                self.__alarmTimerLabel.SetForegroundColour(self.__labelAlarmForeground)
            self.__alarmUpdateTimer.Stop()
            
    
    def startTimer(self):
        """
        Start the AlarmTimer
        If the timer was already running, it is reset before start
        """
        self.reset()
        self.__displayUpdateTimer.Start(self.__displayUpdateTimerInterval);
        self.__startTime = time.time()
        if DEBUG: print "Timer started at time %s"%(time.ctime(self.__startTime))
            
    def reset(self):
        """
        reset the AlarmTimer
        All the timers are stopped and the display is put back to 0
        """
        self.__displayUpdateTimer.Stop()
        self.__alarmUpdateTimer.Stop()
        self.__alarmTrippedTime = None
        self.__alarmTripped = False
        self.__startTime = None
        self.__alarmTimerLabel.SetLabel("00:00")
        self.__alarmTimerLabel.SetForegroundColour(self.__labelNormalForeground)
    
    def __onLeftDClick(self, event):
        """
        Event handler for a double click on the AlarmTimer
        This action resets the timer
        """
        self.reset()

if __name__ == "__main__":
    app = wx.PySimpleApp(0)
    wx.InitAllImageHandlers()
    AlarmTimerFrame = AlarmTimer(None, -1, "")
    app.SetTopWindow(AlarmTimerFrame)
    AlarmTimerFrame.Show()
    AlarmTimerFrame.setAlarmSoundFile("./media/beep-01.wav")
    AlarmTimerFrame.startTimer()
    print AlarmTimerFrame.GetSize()
    app.MainLoop()
    