use warnings;
use strict;

=head1 NAME

BarnOwl::Module::Jabber::ConnectionManager

=head1 DESCRIPTION

A class to keep track of all the active connection in the BarnOwl
Jabber module

=cut

package BarnOwl::Module::Jabber::ConnectionManager;

sub new {
    my $class = shift;
    return bless { }, $class;
}

sub addConnection {
    my $self = shift;
    my $jidStr = shift;

    my $client = BarnOwl::Module::Jabber::Connection->new;

    $self->{$jidStr}->{Client} = $client;
    $self->{$jidStr}->{Roster} = $client->Roster();
    $self->{$jidStr}->{Status} = "available";
    return $client;
}

sub removeConnection {
    my $self = shift;
    my $jidStr = shift;
    return 0 unless exists $self->{$jidStr};

    BarnOwl::remove_io_dispatch($self->{$jidStr}->{Client}->{fileno}) if $self->{$jidStr}->{Client}->{fileno};
    $self->{$jidStr}->{Client}->Disconnect()
      if $self->{$jidStr}->{Client};
    delete $self->{$jidStr};

    return 1;
}

sub scheduleReconnect {
    my $self = shift;
    my $jidStr = shift;
    return 0 unless exists $self->{$jidStr};
    BarnOwl::admin_message(Jabber => "Disconnected from jabber account $jidStr");

    unless (BarnOwl::getvar('jabber:reconnect') eq 'on') {
        return $self->removeConnection($jidStr);
    }

    BarnOwl::remove_io_dispatch($self->{$jidStr}->{Client}->{fileno}) if $self->{$jidStr}->{Client}->{fileno};
    $self->{$jidStr}->{Client}->Disconnect()
      if $self->{$jidStr}->{Client};

    $self->{$jidStr}->{Status} = "reconnecting";
    $self->{$jidStr}->{ReconnectBackoff} = 5;
    $self->{$jidStr}->{ReconnectAt} = time + $self->{$jidStr}->{ReconnectBackoff};
    return 1;
}

sub setAuth {
    my $self = shift;
    my $jidStr = shift;
    $self->{$jidStr}->{Auth} = shift;
}

sub tryReconnect {
    my $self = shift;
    my $jidStr = shift;
    my $force = shift;

    return 0 unless exists $self->{$jidStr};
    return 0 unless $self->{$jidStr}{Status} eq "reconnecting";
    return 0 unless $force or (time > $self->{$jidStr}{ReconnectAt});

    $self->{$jidStr}->{ReconnectBackoff} *= 2;
    $self->{$jidStr}->{ReconnectBackoff} = 60*5
        if $self->{$jidStr}->{ReconnectBackoff} > 60*5;
    $self->{$jidStr}->{ReconnectAt} = time + $self->{$jidStr}->{ReconnectBackoff};

    my $client = $self->{$jidStr}->{Client};
    my $status = $client->Connect;
    return 0 unless $status;

    my @result = $client->AuthSend( %{ $self->{$jidStr}->{Auth} } );
    if ( !@result || $result[0] ne 'ok' ) {
        $self->removeConnection($jidStr);
        BarnOwl::error( "Error in jabber reconnect: " . join( " ", @result ) );
        return 0;
    }
    $self->{$jidStr}->{Roster} = $client->Roster();
    $self->{$jidStr}->{Status} = "available";
    $client->onConnect($self, $jidStr);
    foreach my $muc ($client->MUCs()) {
        $muc->Join($muc->{ARGS});
    }

    return 1;
}

sub renameConnection {
    my $self = shift;
    my $oldJidStr = shift;
    my $newJidStr = shift;
    return 0 unless exists $self->{$oldJidStr};
    return 0 if $oldJidStr eq $newJidStr;

    $self->{$newJidStr} = $self->{$oldJidStr};
    delete $self->{$oldJidStr};
    return 1;
}

sub connected {
    my $self = shift;
    return scalar keys %{ $self };
}

sub getJIDs {
    my $self = shift;
    return keys %{ $self };
}

sub jidExists {
    my $self = shift;
    my $jidStr = shift;
    return exists $self->{$jidStr};
}

sub baseJIDExists {
    my $self = shift;
    my $jidStr = shift;
    my $jid = new Net::Jabber::JID;
    $jid->SetJID($jidStr);
    my $baseJID = $jid->GetJID('base');

    foreach my $cjidStr ( keys %{ $self } ) {
        my $cJID = new Net::Jabber::JID;
        $cJID->SetJID($cjidStr);
        return $cjidStr if ($cJID->GetJID('base') eq $baseJID);
    }
    return 0;
}

sub jidActive {
    my $self = shift;
    my $jidStr = shift;
    return(exists $self->{$jidStr} and $self->{$jidStr}{Status} eq "available");
}

sub sidExists {
    my $self = shift;
    my $sid = shift || "";
    foreach my $c ( values %{ $self } ) {
        return 1 if ($c->{Client}->{SESSION}->{id} eq $sid);
    }
    return 0;
}

sub getConnectionFromSid {
    my $self = shift;
    my $sid = shift;
    foreach my $c (values %{ $self }) {
        return $c->{Client} if $c->{Client}->{SESSION}->{id} eq $sid;
    }
    return undef;
}

sub getConnectionFromJID {
    my $self = shift;
    my $jid = shift;
    $jid = $jid->GetJID('full') if UNIVERSAL::isa($jid, 'Net::XMPP::JID');
    return $self->{$jid}->{Client} if $self->jidActive($jid);
}

sub getRosterFromSid {
    my $self = shift;
    my $sid = shift;
    foreach my $c (values %{ $self }) {
        return $c->{Roster}
          if $c->{Client}->{SESSION}->{id} eq $sid;
    }
    return undef;
}

sub getRosterFromJID {
    my $self = shift;
    my $jid = shift;
    $jid = $jid->GetJID('full') if UNIVERSAL::isa($jid, 'Net::XMPP::JID');
    return $self->{$jid}->{Roster} if $self->jidExists($jid);
}


1;
