# $File: //depot/libOurNet/BBS/lib/OurNet/BBS/MAPLE2/SessionGroup.pm $ $Author: autrijus $
# $Revision: #2 $ $Change: 3792 $ $DateTime: 2003/01/24 19:34:06 $

package OurNet::BBS::MAPLE2::SessionGroup;

use strict;
no warnings 'deprecated';
use fields qw/bbsroot shmkey maxsession chatport passwd shmid shm _ego _hash/;
use constant IsWin32 => ($^O eq 'MSWin32');
use constant SIGUSR2 => IsWin32 ? 'INT' : 'USR2';

use OurNet::BBS::Base (
    '%registered' => {},
    '%instances'  => {},
    '$packstring' => 'LLLLLCCCx1LCCCCZ13Z11Z20Z24Z29Z11a256a64LCx3a1000LL',
    '$packsize'   => 1476,
    '@packlist'   => [
        qw/uid pid sockaddr destuid destuip active invisible
           sockactive userlevel mode pager in_chat sig userid
           chatid realname username from tty friends reject
           uptime msgcount msgs mood site/
    ],
);

use OurNet::BBS::ShmScalar;

sub message_handler {
    # we don't handle multiple messages in the queue yet.
    foreach my $instance (values %instances) {
        print "check for instance $instance\n" if $OurNet::BBS::DEBUG;
        $instance->refresh_meta($_)
            foreach (0..$instance->{maxsession}-1);

        foreach my $session (values %{$registered{$instance}}) {
            print "check for $session->{_hash}{pid}\n" if $OurNet::BBS::DEBUG;
            $session->refresh_meta();
            if ($session->{_hash}{msgcount}) {
                my ($pid, $userid, $message) =
                    unpack('LZ13Z80x3', $session->{_hash}{msgs});
                my $from = $pid && (grep {$_->{pid} == $pid}
                    @{$instance->{_hash}}{0..$instance->{maxsession}-1})[0];
                print "pid $pid, from $from\n" if $OurNet::BBS::DEBUG;
                $session->dispatch($from || $userid, $message);
            }
        }
    }

    $SIG{+SIGUSR2} = \&message_handler if SIGUSR2();
};

$SIG{+SIGUSR2} = \&message_handler if SIGUSR2();

sub _lock {}
sub _unlock {}

sub shminit {
    my $self = shift;

    if ($^O ne 'MSWin32' and
	$self->{shmid} = shmget($self->{shmkey},
				($self->{maxsession})*$packsize+36, 0)) {
      tie $self->{shm}{uptime}, 'OurNet::BBS::ShmScalar',
	$self->{shmid}, $self->{maxsession}*$packsize, 4, 'L';
      tie $self->{_hash}{number}, 'OurNet::BBS::ShmScalar',
	$self->{shmid}, $self->{maxsession}*$packsize+4, 4, 'L';
      tie $self->{shm}{busystate}, 'OurNet::BBS::ShmScalar',
	$self->{shmid}, $self->{maxsession}*$packsize+8, 4, 'L';
      $instances{$self} = $self;
    }
}

sub refresh_meta {
    my ($self, $key) = @_;

    $self->shminit unless ($self->{shmid} || !$self->{shmkey});

    if ($key eq int($key)) {
        print "new toy called $key\n" 
	    if !$self->{_hash}{$key} and $OurNet::BBS::DEBUG;

        $self->{_hash}{$key} ||= $self->module('Session')->new({
	      recno	=> $key,
	      shmid	=> $self->{shmid},
	      shm	=> $self->{shm},
	      chatport	=> $self->{chatport},
	      registered=> $registered{$self} ||= {},
	      passwd	=> $self->{passwd},
	});

        return;
    }
}

sub STORE {
    my ($self, $key, $value) = @_;

    die "STORE: attempt to store non-hash value ($value) into ".ref($self)
        unless UNIVERSAL::isa($value, 'HASH');

    unless (length($key)) {
        print "trying to create new session\n" if $OurNet::BBS::DEBUG;

        undef $key;
        for my $newkey (0..$self->{maxsession}-1) {
            $self->refresh_meta($newkey);
            ($key ||= $newkey, last) if $self->{_hash}{$newkey}{pid} < 2;
        }
        print "new key $key...\n" if $OurNet::BBS::DEBUG;
    }

    die "no more session $key" unless defined $key;

    ++$self->{_hash}{number};
    $self->refresh_meta($key);
    %{$self->{_hash}{$key}} = %{$value};

}

sub DESTROY {
    my $self = shift;
    delete $instances{$self};
}

1;