use Irssi;
use strict;
use vars qw($VERSION %IRSSI);

$VERSION = "0.0.9";

%IRSSI = (
    authors     =>  "Sebastian Groeneveld",
    contact     =>  "dracuul78\@hotmail.com",
    name        =>  "away_notice",
    description =>  "Displays a notice when status is set to away or back",
    license     =>  "GPL v2 and any later",
    url         =>  "http://dracuul.tripod.com/",
);

# ----------------------------
#     Purpose
# ----------------------------
#
# When you mark yourself as "away", it displays a notice in pre-selected channels
# with your away message, or a custom message. When you come "back" it displays a
# notice and, optionally, the time you've been away. It does not require the use
# of any custom away command, which makes it compatible with other scripts that
# change your away status, eg. screen_away.
#
# 
# ----------------------------
#     Configuration
# ----------------------------
#
# To see a list of available options you can type: /SET away_notice
# Here's what they do:
# 
# away_notice_active (default: ON)
# 
#     Set this to OFF to disable the announcements when you go away or come back.
#
# away_notice_reply (default: ON)
#
#     Set this to OFF to disable the replies that will be displayed whenever
#     mentions your nickname while you're away.
#
# away_notice_show_status (default: ON)
# 
#     Set this to OFF to disable the messages in the status window. If this setting
#     is turned on, it will show messages like this:
#     -!- away_notice: Sent away notice (freenode)
#
# away_notice_message (default: "")
# 
#     By default, the notice contains your away message, but you can override that
#     with this setting: if you specify an away_notice_message, this message will
#     be displayed in the notice instead. You can use your away message again by
#     clearing this setting: /SET -clear away_notice_message
#
# away_notice_message_once (default: "")
# 
#     Sometimes you may want to display a custom message in your notice only one
#     time. If you specify an away_notice_message_once, this message will be used
#     in the notice, so it also overrides the away_notice_message. Whenever you
#     come back this setting is cleared.
#
# away_notice_type (default: "notice")
#
#     Specifies in which format the away/back announcement is shown in the channel.
#     This can be either "notice", "action" or "message".
#
# away_notice_reply_type (default: "action")
#
#     Specified in which format the reply is shown in the channel. This can be
#     either "notice", "action" or "message".
#
# away_notice_allow_channels (default: ^$)
# 
#     You can specify the channels that should receive a announcement. Depending on
#     the value of away_notice_order_channels, this setting will either specify the
#     channel names that DO or DON'T receive an announcement.
#     You should use a regular expression, for example:
#     /SET away_notice_allow_channels Freenode:#irssi|DALnet:#debian|IRCNet:*
#
# away_notice_order_channels (default: "exclude")
# 
#     If this setting is set to "exclude", it will send an announcement to all
#     channels *except* the ones specified in away_notice_allow_channels.
#     If set to "allow", it will be sent to *only* those channels specified
#     in away_notice_allow_channels.
#
# away_notice_reply_allow_channels (default: ^$)
# 
#     The same as away_notice_allow_channels (see above), but then for the replies
#     instead of the announcements.
#
# away_notice_reply_order_channels (default: "exclude")
#
#     The same as away_notice_order_channels (see above), but then for the replies
#     instead of the announcements.
#
# away_notice_time_texts (default: "wk,wks,day,days,hr,hrs,min,mins,sec,secs")
# 
#     These are the words used when displaying the away time. You can translate
#     them, or replace them will the full words, as long as you don't change the
#     order.
#
# away_notice_show_time (default: ON)
# 
#     If set to OFF, there will be no away time in the announcement when you're back.
#
# away_notice_reply_timeout (default: 1800)
#
#     Minimum number of seconds between two replies in the same channel. If
#     people keep mentioning your nick, this timeout prevents replies being
#     sent every time. The away announcement (if any) also counts.
#
# away_notice_gone_text (default: "is gone: ")
# 
#     The first part of the away notice is specified by this setting. The away
#     message (or away_notice_message[_once]) will be appended.
#
# away_notice_back_text (default: "has returned")
# 
#     The first part of the back notice is specified by this setting. The away
#     time will be appended, if away_notice_show_time is set to ON.
#
# away_notice_back_text2 (default: "after")
#
#     If away_notice_show_time is set to ON, this text will be displayed between 
#     just before the away time.
#
#
# ----------------------------
#     Example
# ----------------------------
#
# Your away notice will look like this:
# 
# -dracula:#irssi- is gone: sleeping
#                  \______/ \______/
#                     \          \__ away message or away_notice_message[_once]
#                      \
#                       \__ away_notice_gone_text
#
# Your back notice will look like this:
# 
# -dracula:#irssi- has returned after 14 mins, 23 secs
#                  \__________/ \___/ \______________/
#                     \           \           \_ if away_notice_show_time = ON
#                      \           \_ away_notice_back_text2
#                       \__ away_notice_back_text
#
#
# ----------------------------
#     Special thanks
# ----------------------------
#
# ... to Wouter Coekaerts for his away_verbose script, which has served as a
#     great example for this script.
# 
# ... to Jonathan Black for coming up with the ideas and helping me with testing.
# 
# Now you can stop reading and load the script :)

#
# Status messages
# 
Irssi::theme_register(
[
 'away_notice_crap', '{line_start}{hilight ' . $IRSSI{'name'} . ':} $0', 
 'away_notice_loaded', '%R>>%n %_Scriptinfo:%_ Loaded "$0" version $1 by $2.'
]);

#
# Set default values for global variables
# 
my $away_time_texts = "wk,wks,day,days,hr,hrs,min,mins,sec,secs";
my %away_time;
my %reply_time;

#
# Add irssi settings 
# 
Irssi::settings_add_bool( 'misc', $IRSSI{'name'} . '_active',               1 );
Irssi::settings_add_bool( 'misc', $IRSSI{'name'} . '_reply',                1 );
Irssi::settings_add_bool( 'misc', $IRSSI{'name'} . '_show_status',          1 );
Irssi::settings_add_str ( 'misc', $IRSSI{'name'} . '_type',                 "notice");
Irssi::settings_add_str ( 'misc', $IRSSI{'name'} . '_reply_type',           "action");
Irssi::settings_add_str ( 'misc', $IRSSI{'name'} . '_reply_timeout',        1800 );
Irssi::settings_add_bool( 'misc', $IRSSI{'name'} . '_show_time',            1 );

Irssi::settings_add_str ( 'away', $IRSSI{'name'} . '_allow_channels',       "^\$" );
Irssi::settings_add_str ( 'away', $IRSSI{'name'} . '_order_channels',       "exclude" );
Irssi::settings_add_str ( 'away', $IRSSI{'name'} . '_reply_allow_channels', "^\$" );
Irssi::settings_add_str ( 'away', $IRSSI{'name'} . '_reply_order_channels', "exclude" );
Irssi::settings_add_str ( 'away', $IRSSI{'name'} . '_message',              "" );
Irssi::settings_add_str ( 'away', $IRSSI{'name'} . '_message_once',         "" );
Irssi::settings_add_str ( 'away', $IRSSI{'name'} . '_time_texts',           $away_time_texts );
Irssi::settings_add_str ( 'away', $IRSSI{'name'} . '_gone_text',            "is gone:" );
Irssi::settings_add_str ( 'away', $IRSSI{'name'} . '_back_text',            "has returned" );
Irssi::settings_add_str ( 'away', $IRSSI{'name'} . '_back_text2',           "after");

#
# Convert number of seconds to a displayable string
# Copied from 'away_verbose' script made by Wouter Coekaerts
# 
sub secs2text {
    $away_time_texts = Irssi::settings_get_str($IRSSI{'name'} . '_time_texts');
    my ($secs) = @_;
    my ($wk_,$wks_,$day_,$days_,$hr_,$hrs_,$min_,$mins_,$sec_,$secs_) = (0,1,2,3,4,5,6,7,8,9,10);
    my @texts = split(/,/,$away_time_texts);
    my $mins = int($secs/60); $secs -= ($mins*60);
    my $hrs = int($mins/60); $mins -= ($hrs*60);
    my $days = int($hrs/24); $hrs -= ($days*24);
    my $wks = int($days/7); $days -= ($wks*7);
    my $text = (($wks>0) ? (($wks>1) ? "$wks $texts[$wks_] " : "$wks $texts[$wk_] ")  : "" );
    $text .= (($days>0) ? (($days>1) ? "$days $texts[$days_] " : "$days $texts[$day_] ")  : "" );
    $text .= (($hrs>0) ? (($hrs>1) ? "$hrs $texts[$hrs_] " : "$hrs $texts[$hr_] ")  : "" );
    $text .= (($mins>0) ? (($mins>1) ? "$mins $texts[$mins_] " : "$mins $texts[$min_] ")  : "" );
    $text .= (($secs>0) ? (($secs>1) ? "$secs $texts[$secs_] " : "$secs $texts[$sec_] ")  : "" );
    $text =~ s/ $//;
    return $text;
}


#
# Send a notice to specified channel on specified server, if allowed
#
sub send_notice {
    my ($server,$channelname,$text,$reply) = @_;
		
    # Read settings, depending on whether this is a reply notice or a mode change notice
    my $away_allow_channels;
    my $away_order_channels;
    my $type;
	
    if ($reply) {
        $away_allow_channels = Irssi::settings_get_str($IRSSI{'name'} . '_reply_allow_channels');
        $away_order_channels = Irssi::settings_get_str($IRSSI{'name'} . '_reply_order_channels');
        $type                = Irssi::settings_get_str($IRSSI{'name'} . '_reply_type');
    } else {
        $away_allow_channels = Irssi::settings_get_str($IRSSI{'name'} . '_allow_channels');
        $away_order_channels = Irssi::settings_get_str($IRSSI{'name'} . '_order_channels');
        $type                = Irssi::settings_get_str($IRSSI{'name'} . '_type');
    }

    my $id = $server->{chatnet} . ":" . $channelname;
    my $last_reply_time = $reply_time{$server->{chatnet}}{$channelname};

    # Are we allowed to send a notice to this channel?
    return unless (($id =~ /$away_allow_channels/i) != ($away_order_channels eq "exclude"));

    # Check time since previous notice to this channel
    if (time - $last_reply_time < Irssi::settings_get_str($IRSSI{'name'} . '_reply_timeout')) {
        if (Irssi::settings_get_bool($IRSSI{'name'} . '_show_status') == 1) {
            Irssi::printformat(MSGLEVEL_CLIENTCRAP, 'away_notice_crap', "Suppressed away reply (" .$id .")");
        }
        return;
    }

    # Send the notice
    if ($type eq "notice") {
        $server->command("NOTICE $channelname $text");
    } elsif ($type eq "action") {
        $server->command("DESCRIBE $channelname $text");
    } else {
        $server->command("MSG $channelname $text");
    }

    # And remember the time
    $reply_time{$server->{chatnet}}{$channelname} = time;
}

#
# Send a notice to channels on specified server
# 
sub send_notice_to_channels {
    my ($server,$text) = @_;

    foreach my $chan ($server->channels) {
        send_notice($server, $chan->{name}, $text, 0);
    }
}

#
# Return text for away notice
#
sub away_text {
    my ($server) = @_;

    # By default, use the existing away reason
    my $message = $server->{away_reason};

    # Has the user specified a custom (standard) away message?
    my $override = Irssi::settings_get_str($IRSSI{'name'} . '_message');
    if (length($override) > 0) {
        $message = $override;
    }

    # Has the user specified a single-time away message?
    $override = Irssi::settings_get_str($IRSSI{'name'} . '_message_once');
    if (length($override) > 0) {
    	$message = $override;
    }

    # Return the 'away' notice
    return Irssi::settings_get_str($IRSSI{'name'} . '_gone_text') . ' ' . $message;
}

#
# Called whenever away mode is changed
#
sub sig_away_mode_changed {
    my ($server) = @_;
    my $tag = $server->{tag};

    if ($server->{usermode_away} == 1) {	
        # We are away
		
        # Remember the time we went away on this server
        $away_time{ $tag } = time;

        if (Irssi::settings_get_bool($IRSSI{'name'} . '_active') == 1) {
		
            # Send a status message
            if (Irssi::settings_get_bool($IRSSI{'name'} . '_show_status') == 1) {
                Irssi::printformat(MSGLEVEL_CLIENTCRAP, 'away_notice_crap', "Sent away notice (" .$tag .")");
            }
			
            # Send the 'away' notice to channels on this server
            my $text = away_text($server);
            send_notice_to_channels($server, $text);
        }

    } else {
        # We are back
	
        # Retrieve time we went away on this server, and clean up
        my $server_away_time = $away_time{ $tag };
        if ($server_away_time > 0) {
            delete( $away_time{ $tag } );
        }

        # Clear the single-time away message
        Irssi::settings_set_str($IRSSI{'name'} . '_message_once', "");
		
        # Reset all reply times for this server, so the back notice will surely be displayed
        foreach my $chan ( keys %{ $reply_time{$server->{chatnet}} } ) {
            $reply_time{$server->{chatnet}}{$chan} = 0 ;
        }
								
        if (Irssi::settings_get_bool($IRSSI{'name'} . '_active') == 1) {
			
            # Send a status message
            if (Irssi::settings_get_bool($IRSSI{'name'} . '_show_status') == 1) {
                Irssi::printformat(MSGLEVEL_CLIENTCRAP, 'away_notice_crap', "Sent back notice (" .$tag . ")");
            }
			
            my $text = Irssi::settings_get_str($IRSSI{'name'} . '_back_text');
            my $after = Irssi::settings_get_str($IRSSI{'name'} . '_back_text2');
			
            # Append away-time to notice
            if ($server_away_time > 0) {
                if (Irssi::settings_get_bool($IRSSI{'name'} . '_show_time') == 1) {
                    if (length($after) > 0) {
                        $text .= " " . $after;
                    }
                    $text .= " " . secs2text(time - $server_away_time);
                }
            }

            # Send the 'back' notice to channels on this server
            send_notice_to_channels($server, $text);
        }

        # Remove all reply times for this server
        delete($reply_time{$server->{chatnet}});
    }
}


#
# Reply with away message if our nick is mentioned
#
sub sig_event_privmsg {
    my ($server, $data, $nick, $address) = @_;
    my ($target, $msg) = split / :/,$data,2;

    # Ignore if we're not away
    return unless $server->{usermode_away};

    # Ignore if replies are disabled
    return unless Irssi::settings_get_bool($IRSSI{'name'} . '_reply');

    # Check whether our nick was mentioned
    my $own_nick = $server->{nick};
    $own_nick =~ s/\W//g;
    return unless $msg =~ /^(\Q$server->{nick}\E|\Q$own_nick\E)\s*[,:].+/i;
	
    my $text = away_text($server);
    send_notice($server, $target, $text, 1);
}

#
# Install signal handler
# 
Irssi::signal_add     ( 'away mode changed', \&sig_away_mode_changed );
Irssi::signal_add_last( 'event privmsg',     \&sig_event_privmsg );
#Irssi::printformat    (MSGLEVEL_CLIENTCRAP, 'away_notice_loaded', $IRSSI{name}, $VERSION, $IRSSI{authors});