#!/usr/bin/perl
##
## Pidgin "Office Hours" plugin by Kev 'Kyrian' Green.
##
## This project is sponsored by oRe Net [http://www.orenet.co.uk/],
## providers of Linux solutions, whatever your linux server problem,
## you can count on us to fix it.
##
## $Id: pidgin-office-hours.pl,v 1.2 2011-11-21 15:47:00 kyrian Exp $

use Getopt::Long;
use Purple; # Script barfs if this is not installed, and I can't see how to allow 'help' to work even if it is not installed???
use POSIX; # Needed for mktime() else nothing works.

my $help = undef;
my $rv = GetOptions( 'help|h' => \$help );

# To show status info, because it seemed like a good idea.
my $tt = 1;
my %tick_tock = (
   0 => 'tick',
   1 => 'tock'
);

## First add a help option to find out what the file does if it's called
## directly. It shouldn't be, but it'd be helpful to have such a thing.
if ($help) {
        print qq|
This is a plugin for Pidgin. Install it in your ~/.pidgin/plugins/ directory
and restart Pidgin.

Don't run this directly, there is no point ;-)
|;
        exit;
}


## Our information structure.
%PLUGIN_INFO = (
    perl_api_version => 2,
    name => 'Office Hours', ##  Plugin
    version => '$Revision: 1.1 $',
    summary => 'Perl plugin to make you logout when you\'re not there',
    description => 'It makes you log out when you are not there, hopefully.',
    author => 'Kev \'Kyrian\' Green <kyrian\@ore.org>',
    url => 'http://www.orenet.co.uk/',

    load => 'plugin_load',
    unload => 'plugin_unload',
    prefs_info => 'plugin_prefs_cb'
);

## How often we 'tick' into active state, not too often, but not too seldom,
## I hope.
#$tick_int_secs = 900; # 15 minutes.
#my $tick_int_secs = 60; # 1 minute, while testing.
my $tick_int_secs = 300; # 5 minutes, while testing.

#$tick_int = $tick_int_secs * 1000; # wtf. is it seconds, miliseconds or what, I can't Google a straight answer!
my $tick_int = $tick_int_secs; # It *is* in seconds.

## Start callback.
sub plugin_init {
        return %PLUGIN_INFO;
}

## Close callback.

## Timed/idle callback. It seems this may need to be defined before "plugin_load" (?)
sub plugin_periodic {
        my $plugin = shift;

        #Purple::Debug::info("officehours", "plugin_periodic() called.\n");

        ## Did we get woken up when we should not be in the office?
        my ($sec, $min, $h, $d, $m, $y, $wday, $yday, $isdist) = localtime();

        #Purple::Debug::info("officehours", "plugin_periodic() has fetched time ".$h.":".$min.":".$sec." and weekday ".$wday.".\n");
        
        ## We should do type casting here, but I'll rely on perl's internals
        ## for now.
        if (plugin_prefs_vrfy()) {
            #Purple::Debug::info("officehours", "plugin_periodic() has verified prefs.\n");
            
            my $str_min_end = Purple::Prefs::get_string("/plugins/core/perl_office_hours/string_min_end");
            my $str_hr_end = Purple::Prefs::get_string("/plugins/core/perl_office_hours/string_hr_end");
            my $str_min_start = Purple::Prefs::get_string("/plugins/core/perl_office_hours/string_min_start");
            my $str_hr_start = Purple::Prefs::get_string("/plugins/core/perl_office_hours/string_hr_start");

            #Purple::Debug::info("officehours", "plugin_periodic() has fetched prefs for comparison.\n");

            ## Create utility reference times.
            my $end_ux = mktime(0,
                    $str_min_end,
                    $str_hr_end,
                    $d, $m, $y, $wday, $yday);

            #Purple::Debug::info("officehours", "plugin_periodic() has created utility end time for comparison.\n");

            my $start_ux = mktime(0,
                    $str_min_start,
                    $str_hr_start,
                    $d, $m, $y, $wday, $yday);

            #Purple::Debug::info("officehours", "plugin_periodic() has created utility start time for comparison.\n");
    
            ## Perhaps we should check that $end_ux, and $start_ux came out
            ## valid here?
     
            ## Only on weekdays??
            if ($wday > 0 && $wday < 6) { ## Erm.. Is sunday 0, or 7, etc??
                    ## Then check the time.
                    if (($h > $str_hr_end
                            && $min > $str_min_end) ||
                    ($h < $str_hr_start
                            && $min < $str_min_start)) {
                            Purple::Debug::info("officehours","Running disconnect_all() due to current time ".$h.":".$min.":".$sec." and office hours being ".$str_hr_start.":".$str_min_start.":00 to ".$str_hr_end.":".$str_min_end.":00\n");
                            ## If so, log out any logged-in accounts...
                            disconnect_all();
                    }
            }

            ## Do our stuff here to calculate the next 'wake up' time
            #my $next_secs = 10 * $to_secs_multi; ## We need to calculate this from the timestamp etc.
            my $next_tick = $tick_int;
            
            do_tick_tock();
            
            ## Wake ourselves up then (timeout/$next_secs is measured in seconds)
            #Purple::Debug::info("officehours","Scheduled next tick with interval ".$tick_int.".\n");
            Purple::timeout_add($plugin, $next_tick, \&plugin_periodic, $plugin);
        } else {
            Purple::Debug::info("officehours","Deactivated Office Hours Plugin due to invalid configuration.\n");
        }

}

## "Load me" callback. We check config and set up the initial callback here.
sub plugin_load {
   my $plugin = shift;

   # It doesn't work without a root node for your plugin prefs??
   Purple::Prefs::add_none("/plugins/core/perl_office_hours");

   # Start and end hour/min of your office hours.
   Purple::Prefs::add_string("/plugins/core/perl_office_hours/string_hr_start","9");
   Purple::Prefs::add_string("/plugins/core/perl_office_hours/string_min_start", "00");
   Purple::Prefs::add_string("/plugins/core/perl_office_hours/string_hr_end","17");
   Purple::Prefs::add_string("/plugins/core/perl_office_hours/string_min_end", "00");

   if (plugin_prefs_vrfy()) {
       ## We must set our initial tick call, or we will never actually do anything ;-)
       ## (timeout/$next_secs is measured in seconds from the current time)
       do_tick_tock();
       Purple::timeout_add($plugin, $tick_int, \&plugin_periodic, $plugin);
       Purple::Debug::info("officehours","Loaded and Activated Office Hours Plugin with interval ".$tick_int.".\n");
   } else {
       Purple::Debug::info("officehours","Did not activate Office Hours Plugin due to invalid configuration");
   }
   
}

# Function to generate the Pidgin preferences screen/tab.
#
# Note: If you save a preference as eg. a boolean, the pref will remain a
# boolean every time it's reloaded even if the type has changed here, and
# you have to edit .purple/prefs.xml or similar to change the type manually.
sub plugin_prefs_cb {

   #Purple::Debug::info("officehours","Office Hours Plugin: Prefs Start\n");

   $frame = Purple::PluginPref::Frame->new();

   # @todo verify that re-use of $ppref is OK and doesn't screw things up.

   # @todo a dropdown for the hour is probably better, not sure about minute?

   $ppref = Purple::PluginPref->new_with_name_and_label(
          "/plugins/core/perl_office_hours/string_hr_start", "Office Start Hour (0-23)");
   $ppref->set_type(2);
   $ppref->set_max_length(2);
   $frame->add($ppref);

   $ppref = Purple::PluginPref->new_with_name_and_label(
          "/plugins/core/perl_office_hours/string_min_start", "Office Start Minute (0-59)");
   $ppref->set_type(2);
   $ppref->set_max_length(2);
   $frame->add($ppref);

   $ppref = Purple::PluginPref->new_with_name_and_label(
          "/plugins/core/perl_office_hours/string_hr_end", "Office End Hour (0-23)");
   $ppref->set_type(2);
   $ppref->set_max_length(2);
   $frame->add($ppref);

   $ppref = Purple::PluginPref->new_with_name_and_label(
          "/plugins/core/perl_office_hours/string_min_end", "Office End Minute (0-59)");
   $ppref->set_type(2);
   $ppref->set_max_length(2);
   $frame->add($ppref);

   #Purple::Debug::info("officehours","Office Hours Plugin: Prefs End\n");

   return $frame;
}

## Validate the times input, and check that the start hour/min are earlier in
## the day than end hour/min by at least twice our tick interval, otherwise
## strange things might start to happen.
sub plugin_prefs_vrfy {
        my $plugin = shift;

        ## First check validity.
        my $str_min_end = Purple::Prefs::get_string("/plugins/core/perl_office_hours/string_min_end");
        my $str_hr_end = Purple::Prefs::get_string("/plugins/core/perl_office_hours/string_hr_end");
        my $str_min_start = Purple::Prefs::get_string("/plugins/core/perl_office_hours/string_min_start");
        my $str_hr_start = Purple::Prefs::get_string("/plugins/core/perl_office_hours/string_hr_start");

        if ($str_min_start < 0 || $str_min_start > 59) {
            Purple::Debug::info("officehours","Office Hours Plugin - Invalid minute-start value in config.\n");
            return;
        }
        if ($str_min_end < 0 || $str_min_end > 59) {
            Purple::Debug::info("officehours","Office Hours Plugin - Invalid minute-end value in config.\n");
            return;
        }

        if ($str_hr_start < 0 || $str_hr_start > 23) {
            Purple::Debug::info("officehours","Office Hours Plugin - Invalid hour-start value in config.\n");
            return;
        }
        
        if ($str_hr_end < 0 || $str_hr_end > 23) {
            Purple::Debug::info("officehours","Office Hours Plugin - Invalid hour-end value in config.\n");
            return;
        }
        
        ## Then check end is after start.
        return true;
}

## "Unload me" callback. Shouldn't we do more here to prevent memory leaks etc?
sub plugin_unload {
        my $plugin = shift;
        Purple::Debug::info("officehours","Removed Office Hours Plugin.\n");
}

## The actual function to perform disconnection of all live accounts, if the callback
## thinks we should.
sub disconnect_all
{

    #@accounts = Purple::accounts() # Returns a list of all accounts, online or offline.
    my @accounts = Purple::Accounts::get_all(); # Or this?
    
    # Verify if the user is connected (assumption here that disabled accounts
    # cannot be connected??)

    foreach $account (@accounts) {
        #Purple::Debug::info("officehours", "Testing: Purple::Account::is_connected() for ".$account->get_username());
        if ($account->is_connected()) {
            my $status = $account->get_active_status();
            my $statusName = $status->get_name();
            if ($statusName eq "Away") {
                Purple::Debug::info("officehours", $account->get_username()."... Connected + Away. Disconnecting...\n");
                $account->set_status("offline", TRUE);
            } else {
                Purple::Debug::info("officehours", $account->get_username()."... Connected + " . $statusName. ". NOT Disconnecting...\n");
            }
        } else {
           Purple::Debug::info("officehours", $account->get_username()."... Disconnected. Doing nothing.\n");
        }
    }
}

# Debugging function mainly, hence it does nothing right at the moment.
sub do_tick_tock
{
    $tt = 1 - $tt;
    #Purple::Debug::info("officehours",$tick_tock{$tt}."\n");
}           
