#!/usr/bin/python -Wall

import gtk
import gtk.glade
import gobject
import time
import os
import sys
from optparse import OptionParser

class BugMe:
    def __init__(self):
        # Time limit in seconds  (600)
        self.timeLimit = 600
        # First warn the user when this many seconds remain (120)
        self.firstWarn = 120
        # How often to warn initially (secs)
        self.warnInterval = 60
        # How often to warn after time has expired (secs)
        self.annoyInterval = 30
        # Hard time limit; user will be killed after this time
        self.killTime = self.timeLimit * 1.5
        if options.debugMode:
            self.timeLimit = 120
            self.firstWarn = 90
            self.warnInterval = 30
            self.annoyInterval = 10
            self.killTime = 180
        # (foreground, background)
        self.colors = ('black', 'white')
        defaultScreen = gtk.gdk.screen_get_default()
        self.monitorGeometry = defaultScreen.get_monitor_geometry(defaultScreen.get_primary_monitor())
        try:
            self.xml = gtk.glade.XML(options.gladeFile)
        except:
            print "Failed to create GladeXML object."
            # Kill the child
            os.kill(pid, 9)
            sys.exit(255)
        self.startTime = int(time.time())
        self.timerWindow = self.xml.get_widget('TimerWindow')
        self.timerWindow.connect("visibility_notify_event",
                                 self.visibility_event_handler)
        self.timerWindow.set_events(gtk.gdk.VISIBILITY_NOTIFY_MASK)
        self.nagDialog = self.xml.get_widget('NagDialog')
        self.nagLabel = self.xml.get_widget('NagLabel')
        self.elapsed_label = self.xml.get_widget('ElapsedLabel')
        self.elapsed_label.set_markup("<span font_desc=\"50\">00:00</span>")
        self.timer = gobject.timeout_add(1000, self.updateTimer)
        self.xml.signal_autoconnect(self)
        if (options.corner == "NW"):
            # 26 px should allow room for top panel
            self.timerWindow.move(0,26)
        elif (options.corner == "SE"):
            self.timerWindow.set_gravity(gtk.gdk.GRAVITY_SOUTH_EAST)
            width, height = self.timerWindow.get_size()
            self.timerWindow.move(self.monitorGeometry.x + (self.monitorGeometry.width - width), self.monitorGeometry.y + (self.monitorGeometry.height - height))
        self.timerWindow.show_all()
        self.nextWarn = self.startTime + (self.timeLimit - self.firstWarn)
        self.timeExpired = False
        self.timerWindow.modify_bg(gtk.STATE_NORMAL, 
                                   gtk.gdk.color_parse(self.colors[1]))


    def updateTimer(self):
        if pid == os.waitpid(pid, os.WNOHANG)[0]:
            sys.exit(0)
        now = int(time.time())
        elapsed = now - self.startTime
        elapsedTime = (elapsed / 60, elapsed % 60)
        self.elapsed_label.set_markup("<span foreground=\"%s\" background=\"%s\" font_desc=\"50\">%02d:%02d</span>" % (self.colors + elapsedTime))
        self.timerWindow.modify_bg(gtk.STATE_NORMAL, 
                                   gtk.gdk.color_parse(self.colors[1]))
        if elapsed >= self.timeLimit:
            self.colors = ('white', 'red')
            self.warnInterval = self.annoyInterval
            self.timeExpired = True
        if now >= self.nextWarn:
            if elapsed < self.timeLimit:
                self.colors = ('black', 'orange')
            self.nextWarn = now + self.warnInterval
            self.nag(((self.timeLimit - elapsed) / 60, 
                      (self.timeLimit - elapsed) % 60))
        if options.killMode and elapsed >= self.killTime:
            # The user has outstayed their welcome, nuke their session
            print "Quitting."
            sys.exit(0)

	# It's a little odd to put this here, but it doesn't work in
	# __init__; see comments in Trac #386
        self.timerWindow.set_keep_above(True)
        # And the above code doesn't work if the user has an "Always
        # On Top" window above it.  The following does, and bugme lives
        # up to its name:
        if elapsed % 10 == 0:
            self.timerWindow.present()
        return True

    def nag(self, remainingTime):
        if self.timeExpired:
            msg="Please log out immediately."
            if options.killMode:
                msg += "\n\nYour session will be terminated in %d seconds." % (self.killTime - (int(time.time()) - self.startTime))
            self.nagLabel.set_markup("<span font_desc=\"20\">"+msg+"</span>")
        else:
            seconds = "%d second%s" % (remainingTime[1], 
                                       remainingTime[1] != 1 and 's' or '')
            minutes = "%d minute%s" % (remainingTime[0], 
                                       remainingTime[0] != 1 and 's' or '')
            if remainingTime[0] < 1:
                remaining = seconds
            elif remainingTime[1] == 0:
                remaining = minutes
            else:
                remaining = "%s, %s" % (minutes, seconds)
            self.nagLabel.set_markup("<span font_desc=\"20\">You have %s remaining\nin this session.</span>" % (remaining))

        self.nagDialog.show()

    def on_dialog_response(self, dialog, response_id):
        if response_id == gtk.RESPONSE_CLOSE:
            sys.exit(0)
        elif response_id == gtk.RESPONSE_OK:
            self.nagDialog.hide()

    def visibility_event_handler(self, widget, event):
        if event.state != gtk.gdk.VISIBILITY_UNOBSCURED:
            self.timerWindow.present()
        return True


if __name__ == '__main__':
    parser = OptionParser(usage="%prog [--debug] progname [args]",
                          version="%prog 0.1")
    parser.disable_interspersed_args()
    parser.add_option("--debug", 
                     action="store_true", dest="debugMode", default=False,
                     help="Enable debug mode (time limit of 2 minutes)")
    parser.add_option("--fatal",
                     action="store_true", dest="killMode", default=False,
                     help="Kill the user session after 1.5 times the time limit.")
    parser.add_option("--glade", action="store", type="string", 
                      dest="gladeFile",
                      default="/usr/share/bugme/bugme.glade",
                      help="Specify an alternate Glade file for debugging")
    #TODO: --geometry
    parser.add_option("--corner", action="store", type="string", 
                      dest="corner",
                      default="NW",
                      help="Specify a corner for the timer")
    (options, args) = parser.parse_args()
    if options.corner not in ("SE", "NW"):
        print "Sorry, I don't know about the %s corner of the screen" % (options.corner)
        sys.exit(255)
    if not os.access(options.gladeFile, os.R_OK):
        print 'error: Unable to read glade file "' + gladeFile + '"'
        sys.exit(255)
    if len(args) < 1:
        parser.error("'progname' is required")
    pid = os.fork()
    if not pid:
        os.putenv('ATHENA_QUICK', '1')
        try:
            os.execvp(args[0], args)
        except:
            print "error: Could not execvp(%s,%s)" % (args[0], args)
    else:
        BugMe()
        gtk.main()
