package Orangutan::Users;

use strict;

use XML::Parser;
use Orangutan::User;
use Orangutan::Context;

use Data::Dumper;

sub new {
    my $class = shift;

    my $self = { };
    $self->{'users'} = { };
    $self->{'schedule'} = [ ];

    my %args = ( );
    while ($#_ >= 0) {
        $args{lc(pop)} = pop;
    }

    bless $self, $class;

    foreach my $arg (keys %args) {
        if ($arg eq 'directory') {
            $self->Load($args{$arg});
        } else {
            $main::logger->Log("unsupported users property: %s", $arg);
        }
    }

    return $self;
}

sub Add($$) {
    my ($self, $jid) = @_;
    my $user = new Orangutan::User(
        jid => $jid
    );
    $self->{'users'}->{$user->GetJID} = $user;
    return $user;
}

sub Load($$) {
    my ($self, $path) = @_;
    if (opendir(DIR, $path)) {
        my $xml = new XML::Parser;
        while (defined(my $filename = readdir(DIR))) {
            if ($filename =~ /^.+\.xml$/) {
                my $user = new Orangutan::User;
                $xml->setHandlers(
                    Init  => sub { $user->XmlInit(@_); },
                    Start => sub { $user->XmlStart(@_); },
                    Char  => sub { $user->XmlChar(@_); },
                    End   => sub { $user->XmlEnd(@_); },
                    Final => sub { $user->XmlFinal(@_); }
                );
                $xml->parsefile("$path/$filename");
                $self->{'users'}->{$user->GetJID} = $user;
            }
        }
        closedir(DIR);
    } else {
        $main::logger->Log("cannot open users directory");
    }
}

sub Save($) {
    my $self = shift;
    foreach my $user (keys %{$self->{'users'}}) {
        if ($self->{'users'}->{$user}->ShouldSave) {
            $self->{'users'}->{$user}->Save;
        }
    }
}

sub Delete($$) {
    my ($self, $user) = @_;
    if (defined($self->{'users'}->{$user})) {
        unlink($main::config->Get('users', 'directory').'/'.$self->{'users'}->{$user}->GetJabberUserID.'.xml');
        delete($self->{'users'}->{$user});
    }
}

sub Roster($$) {
    my $self = shift;
    my $roster = shift;

    foreach my $jid (keys %{$roster}) {
        if ($roster->{$jid}->{'subscription'} eq 'both') {
            if (!defined($self->{'users'}->{$jid})) {
                my ($id, $login) = $main::query->UserExists($jid);
                if ($id) {
                    my $user = $self->Add($jid);
                    $user->SetUserID($id);
                    $user->SetLogin($login);
                }
            } elsif (!defined($self->{'users'}->{$jid}->GetUserID)) {
                my ($id, $login) = $main::query->UserExists($jid);
                if ($id) {
                    $self->{'users'}->{$jid}->SetUserID($id);
                    $self->{'users'}->{$jid}->SetLogin($login);
                } else {
                    $main::logger->Log("could not get user id for: %s", $self->{'users'}->{$jid}->GetJabberUserID);
                }
            }
        }
    }

    $self->{'roster'} = $roster;
}

sub GetUser($$) {
    my ($self, $jid) = @_;
    if ($jid =~ /\@/) {
        foreach my $user (keys %{$self->{'users'}}) {
            if ($user eq $jid) {
                return $self->{'users'}->{$user};
            }
        }
    } else {
        foreach my $user (keys %{$self->{'users'}}) {
            if ($user =~ /^\Q$jid\E\@/) {
                return $self->{'users'}->{$user};
            }
        }
    }
    return undef;
}

sub GetUserID($$) {
    my ($self, $id) = @_;
    foreach my $user (keys %{$self->{'users'}}) {
        if ($self->{'users'}->{$user}->GetUserID == $id) {
            return $self->{'users'}->{$user};
        }
    }
    return undef;
}

sub GetUserLogin($$) {
    my ($self, $login) = @_;
    foreach my $user (keys %{$self->{'users'}}) {
        if ($self->{'users'}->{$user}->GetLogin eq $login) {
            return $self->{'users'}->{$user};
        }
    }
    return undef;
}

sub GetSchedule($$;$) {
    my ($self, $context, $request) = @_;
    for (my $i = 0; $i < scalar @{$self->{'schedule'}}; $i++) {
        if (($self->{'schedule'}[$i][0]->GetID eq $context->GetID) &&
            (!defined($request) || ($self->{'schedule'}[$i][1] eq $request))) {
            return $self->{'schedule'}[$i];
        }
    }
    return undef;
}

sub GetUsers($) {
    my $self = shift;
    return keys %{$self->{'users'}};
}

sub Init($) {
    my $self = shift;

    foreach my $user (keys %{$self->{'roster'}}) {
        if ($self->{'roster'}->{$user}->{'subscription'} eq 'from') {
            $main::jabber->Subscription(
                type => 'subscribe',
                to   => $user
            );
        }
    }

    foreach my $user (keys %{$self->{'users'}}) {
        $self->{'users'}->{$user}->Init;
    }

    foreach my $context ($main::config->Contexts) {
        if ($context->HasInit) {
            $context->CallInit;
        }
        if ($context->HasSchedule) {
            $context->CallSchedule($self, 'init');
        }
    }
}

sub Schedule($$$;@) {
    my ($self, $time, $context, $request, @args) = @_;

    my $i;
    for ($i = 0; $i < scalar @{$self->{'schedule'}}; $i++) {
        if ($self->{'schedule'}[$i][2] > $time) {
            last;
        }
    }

    my @item = ($context, $request, $time, @args);

    if ($i == 0) {
        unshift(@{$self->{'schedule'}}, \@item);
    } elsif ($i == scalar @{$self->{'schedule'}}) {
        push(@{$self->{'schedule'}}, \@item);
    } else {
        splice(@{$self->{'schedule'}}, $i, 0, \@item);
    }
}

sub CancelSchedule($$;$) {
    my ($self, $context, $request) = @_;

    my $i;
    for ($i = 0; $i < scalar @{$self->{'schedule'}}; $i++) {
        if (($self->{'schedule'}[$i][0]->GetID eq $context->GetID) &&
            (!defined($request) || ($self->{'schedule'}[$i][1] eq $request))) {
            last;
        }
    }

    if ($i < scalar @{$self->{'schedule'}}) {
        splice(@{$self->{'schedule'}}, $i, 1);
    }
}

sub RunContexts($) {
    my $self = shift;

    my $timeout = 0;
    foreach my $user (keys %{$self->{'users'}}) {
        my $utimeout = 0;
        my $time = $self->{'users'}->{$user}->GetTimeout;
        if (($time > 0) && (($utimeout == 0) || ($time < $utimeout))) {
            $utimeout = $time;
        }
        if ($utimeout == 0) {
            if ($self->{'users'}->{$user}->MaySpeak) {
                $time = $self->{'users'}->{$user}->Run;
                if (($time > 0) && (($utimeout == 0) || ($time < $utimeout))) {
                    $utimeout = $time;
                }
            }
            $time = $self->{'users'}->{$user}->GetDelay;
            if (($time > 0) && (($utimeout == 0) || ($time < $utimeout))) {
                $utimeout = $time;
            }
        }
        if (($utimeout > 0) && (($timeout == 0) || ($utimeout < $timeout))) {
            $timeout = $utimeout;
        }
    }
    $self->Save;
    if ($timeout > 0) {
        $timeout = $timeout - time;
        return ($timeout > 0) ? $timeout : 1;
    }
    return undef;
}

sub RunSchedule($) {
    my $self = shift;

    while ($#{$self->{'schedule'}} >= 0) {
        if ($self->{'schedule'}[0][2] <= time) {
            my $schedule = shift(@{$self->{'schedule'}});
            my $context = shift(@{$schedule});
            $context->CallSchedule($self, @{$schedule});
        } else {
            return $self->{'schedule'}[0][2] - time;
        }
    }

    return undef;
}

sub Shutdown($) {
    my $self = shift;
    foreach my $user (keys %{$self->{'users'}}) {
        if ($self->{'users'}->{$user}->IsOnline) {
            $self->{'users'}->{$user}->SetStatus('shutdown');
        }
    }
}

sub TellCreator($$) {
    my ($self, $message) = @_;
    $self->Tell($main::config->Get('admin', 'creator'), $message);
}

sub TellManager($$) {
    my ($self, $message) = @_;
    my $manager = $main::config->Get('admin', 'manager');
    if (ref $manager eq 'ARRAY') {
        my $jid;
        for (my $i = $#{$manager}; $i >= 0; $i--) {
            my $user = $self->GetUser($manager->[$i]);
            if ($user && (!defined($jid) || $user->IsOnline)) {
                $jid = $manager->[$i];
            }
        }
        $manager = $jid;
    }
    $self->Tell($manager, $message);
}

sub Tell($$$) {
    my ($self, $username, $message) = @_;
    my $user = $self->GetUser($username);
    if ($user) {
        $user->SendMessage($message);
    } else {
        return Orangutan::Context::Random(
            "Can't find user you requested...",
            'I cannot find this user...',
            'No such user...'
        );
    }
    return undef;
}

1;
