# -*- Mode: perl; tab-width: 4; indent-tabs-mode: nil; -*-
################################
# Seen Module                  #
################################

package BotModules::Seen;
use vars qw(@ISA);
@ISA = qw(BotModules);
use AnyDBM_File;
use Fcntl;
1;

# SpottedNickChange would be a nice one to do if you 
# can solve the problem of working out which channel
# to say stuff in...

# database for seen data
our $seen = {'times' => {}, 'states' => {}};
# the times that the relevant nicks were last seen active
tie(%{$seen->{'times'}}, 'AnyDBM_File', 'seen-times', O_RDWR|O_CREAT, 0666);
# what the relevant nicks were last seen doing
tie(%{$seen->{'states'}}, 'AnyDBM_File', 'seen-states', O_RDWR|O_CREAT, 0666);

sub Help {
    my $self = shift;
    my ($event) = @_;
    my %commands = {
        'seen' => 'Says how long it\'s been since the last time someone was seen. Syntax: seen victim',
    };
    if ($self->isAdmin($event)) {
        $commands{'mute'} = 'Stop responding to !seen <name> in a channel unless told directly. Syntax: mute seen in <channel>';
        $commands{'unmute'} = 'Start responding to !seen <name> in a channel. Syntax: unmute seen in <channel>';
    }
    return \%commands;
}

# RegisterConfig - Called when initialised, should call registerVariables
sub RegisterConfig {
    my $self = shift;
    $self->SUPER::RegisterConfig(@_);
    $self->registerVariables(
      # [ name, save?, settable? ]
        ['overrides', 1, 1, {'therapist' => 'Look, dude, I\'m feeling fine, mm\'k?'}], # canned responses
        ['maxLines', 1, 1, 5],
        ['directOnlyChannels', 1, 1, []], #list of channels where we're only observing and not responding to !seen unless told.
    );
}

sub Told {
    my $self = shift;
    my ($event, $message) = @_;
    my $now = $event->{'time'};
    $self->{'_lastSpoken'}->{$event->{'user'}} = $now;
    if ($event->{'channel'} ne '') {
        my $channel = $event->{'channel'};
        $seen->{'times'}->{lc $event->{'from'}} = $now;
        $seen->{'states'}->{lc $event->{'from'}} = "saying '$message' to me in $channel.";
    }
    if ($self->isAdmin($event) and $message =~ /^\s*(un)?mute\s+seen\s+in\s+(\S+)\s*$/osi){
        my $mute = !defined($1);
        my $channel = lc $2;
        $channel =~ s/^\#?/\#/;  # Add # character if needed.
        $self->MuteOrUnmuteChannel($event, $mute, $channel);
    }elsif ($message =~ /^\s*!?seen\s+(\S+?)[\s?.!]*$/osi) {
        $self->DoSeen($event, $1);
    } else {
        return $self->SUPER::Told(@_);
    }
    return 0; # we've dealt with it, no need to do anything else.
}

sub Heard {
    my $self = shift;
    my ($event, $message) = @_;
    if ($event->{'channel'} ne '') {
        my $channel = $event->{'channel'};
        $seen->{'times'}->{lc $event->{'from'}} = $event->{'time'};
        $seen->{'states'}->{lc $event->{'from'}} = "saying '$message' in $channel.";
    }
    if (!(grep {$event->{'channel'} eq $_} @{$self->{'directOnlyChannels'}}) and $message =~ /^\s*!seen\s+(\S+)\s*$/osi) {
        $self->DoSeen($event, $1);
    } else {
        return $self->SUPER::Heard(@_);
    }
    return 0; # we've dealt with it, no need to do anything else.
}

sub Felt {
    my $self = shift;
    my ($event, $message) = @_;
    if ($event->{'channel'} ne '') {
        my $nick = $event->{'from'};
        my $channel = $event->{'channel'};
        $seen->{'times'}->{lc $event->{'from'}} = $event->{'time'};
        $seen->{'states'}->{lc $event->{'from'}} = "saying '* $nick $message' in $channel.";
    } else {
        return $self->SUPER::Felt(@_);
    }
    return 0; # we've dealt with it, no need to do anything else.
}

sub Saw {
    my $self = shift;
    my ($event, $message) = @_;
    if ($event->{'channel'} ne '') {
        my $nick = $event->{'from'};
        my $channel = $event->{'channel'};
        $seen->{'times'}->{lc $event->{'from'}} = $event->{'time'};
        $seen->{'states'}->{lc $event->{'from'}} = "saying '* $nick $message' in $channel.";
    } else {
        return $self->SUPER::Felt(@_);
    }
    return 0; # we've dealt with it, no need to do anything else.
}

# SpottedNickChange - Called when someone changes nick
sub SpottedNickChange {
    my $self = shift;
    my ($event, $from, $to) = @_;
    $seen->{'times'}->{lc $event->{'from'}} = $event->{'time'};
    $seen->{'states'}->{lc $event->{'from'}} = "changing nick to $to.";
    return $self->SUPER::SpottedNickChange(@_);
}

sub DoSeen {
    my $self = shift;
    my ($event, $who) = @_;
    my $pattern;
    if (lc $who eq lc $event->{'from'}) {
        $self->say($event, 'You\'re right here, duh!');
    } elsif (lc $who eq lc $event->{'nick'}) {
        $self->say($event, 'I\'m right here, duh!');
    } elsif (defined($self->{'overrides'}->{$who})) {
        $self->say($event, $self->{'overrides'}->{$who});
    } else {
        my $regexp;
        my @nicksToList = ();
        if ($who =~ m!^/(\S+)/$!) {  # shouldn't allow mix and match or blank RE or spaces.
          $regexp = $1;
            my $re = $self->sanitizeRegexp($regexp);  # security + safety first!
            $re = qr/$re/i; #precompile for performance
            if ('' =~ $re){ # will match everything, throw error.
                $self->say($event, 'That pattern matches everything, please be more specific.');
                return;
            }
            @nicksToList = grep {$_ =~ $re} (keys %{$seen->{'times'}});
          $pattern = 1;
        } else {
            if ($who =~ /\*/){  # no point going through the motions if there's no wildcard.
          $regexp = quotemeta(lc $who);
                $regexp =~ s/\\\*/\\S*/g; # replace the escaped * from quotemeta with a \S*  (XXX wanted: the ? wildcard)
                my $re = qr/^$regexp$/;
                if ('' =~ $re){ # will match everything, throw error.
                    $self->say($event, 'That pattern matches everything, please be more specific.');
                    return;
                }
                @nicksToList = grep {$_ =~ $re} (keys %{$seen->{'times'}});
            } else {
                @nicksToList = (lc $who) if defined($seen->{'times'}{lc $who}); # short circuit for the majority of uses
            }
          $pattern = 0;
        }
        if (@nicksToList > $self->{'maxLines'}) { # if it's more than the set threshold, don't flood :)
            $self->say($event,"There are more than $self->{'maxLines'} nicks matching that wildcard, please be more specific.");
        } elsif (@nicksToList > 0) {
            foreach my $nick (@nicksToList) {
              my $seconds = $seen->{'times'}->{$nick};
              $seconds = $event->{'time'} - $seconds;
              my $time = '';
              
              if ($seconds > 90) {
                my $minutes = int $seconds / 60;
                $seconds %= 60;
                if ($minutes > 90) {
                    my $hours = int $minutes / 60;
                    $minutes %= 60;
                    if ($hours > 36) {
                        my $days = int $hours / 24;
                        $hours %= 24;
                        if ($days > 10) {
                            my $weeks = int $days / 7;
                            $days %= 7;
                            if ($weeks > 10) {
                                # good god, nice connection
                            }
                            if ($weeks != 0) {
                                if ($time ne '') {
                                    $time .= ', ';
                                }
                                if ($weeks == 1) {
                                    $time .= "$weeks week";
                                } else {
                                    $time .= "$weeks weeks";
                                }
                            }
                        }
                        if ($days != 0) {
                            if ($time ne '') {
                                $time .= ', ';
                            }
                            if ($days == 1) {
                                $time .= "$days day";
                            } else {
                                $time .= "$days days";
                            }
                        }
                    }
                    if ($hours != 0) {
                        if ($time ne '') {
                            $time .= ', ';
                        }
                        if ($hours == 1) {
                            $time .= "$hours hour";
                        } else {
                            $time .= "$hours hours";
                        }
                    }
                }
                if ($minutes != 0) {
                    if ($time ne '') {
                        $time .= ', ';
                    }
                    if ($minutes == 1) {
                        $time .= "$minutes minute";
                    } else {
                        $time .= "$minutes minutes";
                    }
                }
            }
            if ($seconds == 0) {
                if ($time eq '') {
                    $time .= 'right about now';
                } else {
                    $time .= ' ago';
                }
            } else {
                if ($time ne '') {
                    $time .= ' and ';
                }
                if ($seconds == 1) {
                    $time .= 'a second ago';
                } elsif ($seconds == 2) {
                    $time .= 'a couple of seconds ago';
                } else {
                    $time .= "$seconds seconds ago";
                }
            }
              my $what = $seen->{'states'}->{$nick};
              $self->say($event, "$nick was last seen $time, $what");
            }
        } else {
            my $n = '';
            if ($who =~ /^[aeiou]/o) {
              $n = 'n';
            }
            if ($pattern == 1) {
              $self->say($event, "I've never seen anyone matching the pattern '$who', sorry.");
            } else {
                $self->say($event, "I've never seen a$n '$who', sorry.");
            }
        }
    }
}

sub Unload {
  untie(%{$seen->{'times'}});
  untie(%{$seen->{'states'}});
}

sub MuteOrUnmuteChannel {
    my $self = shift;
    my ($event, $mute, $channel) = @_;
    if ($mute){
        if (grep {$_ eq $channel} @{$self->{'directOnlyChannels'}}){
            $self->say($event,"I'm already ignoring !seen <name> in $channel.");
        } else{
            push @{$self->{'directOnlyChannels'}}, $channel;
            $self->say($event, "I won't respond to !seen <name> in $channel anymore unless told directly.");
            $self->saveConfig();
        }
    } else {
        if (grep {$_ eq $channel} @{$self->{'directOnlyChannels'}}){
            @{$self->{'directOnlyChannels'}} = map {$_ ne $channel} @{$self->{'directOnlyChannels'}};
            $self->say($event,"I'll start responding to !seen <name> in $channel now.");
            $self->saveConfig();
        } else{
            $self->say($event, "I'm already responding to !seen <name> in $channel.");
        }
    }
}