# $Id: Session.pm,v 1.5 2002/08/06 12:31:48 swick Exp $

package Session;
use Connection;
use strict;

our %Session = (
    Handlers => {},		# class event handlers
);

sub new
{
    my ($proto, $conn, $id) = @_;
    my $class = ref($proto) || $proto;
    my $self  = {
	'_connection'	=> $conn,
	'_id'		=> $id,
	'_seq'		=> 0,
    };

    bless ($self, $class);

    $self->trace( "Creating session $id" );

    return $self;
}

sub DESTROY
{
    my $self = shift;
    $self->trace( "Session $self->{_id} destroyed." );
}

sub nextseq
{
    my $self = shift;
    return ++$self->{_seq};
}

sub request($$)
{
    my ($self, $req) = @_;
    my $packet = $self->{_id}."~".$self->nextseq()."~".$req;
    $self->{_connection}->send($packet);
}

sub requestWithReply($$$$)
{
    my ($self, $req, $replyHandler, @handlerParams) = @_;
    my $seq = $self->nextseq();
    my $packet = $self->{_id}."~$seq~".$req;
    $self->{pending}->{$seq} = [$replyHandler, [@handlerParams]];
    $self->{_connection}->send($packet);
}

sub MATrequest($$)
{
    my ($self, $req) = @_;
    return $self->{_connection}->MATsend($req);
}

# returns: 1 if event was passed to some handler, else 0
sub handle
{
    my ($self, $msg) = @_;
    my ($id, $seq, $type, @rest) = split('~', $msg);

    if (ref $self) {
	# check instance handlers first
	if (defined $self->{pending}->{$seq}) {
	    my $replyHandler = $self->{pending}->{$seq}[0];
	    my @handlerParams = @{$self->{pending}->{$seq}[1]};
	    delete $self->{pending}->{$seq};
	    $replyHandler->(@handlerParams, $msg);
	    return 1;
	}
	if (defined $self->{handler}->{$type}) {
	    $self->trace( "Session $id handling $type @rest" );
	    $self->{handler}->{$type}($self, @rest);
	    return 1;
	}
    }

    # not yet handled, or not an instance; see if class can handle
    my $class = ref $self || $self;
    no strict "refs";

    print "searching for $type handler in $class\n";
    if (defined $class->{Handlers}->{$type}) {
	$self->trace( "Class $class handling $msg" );
	$class->{Handlers}->{$type}($self, $msg);
	return 1;
    }

    # this class can't handle; see if superclass can handle
    my $wasHandled = 0;
    my $superClasses = $class."::ISA";

    for my $super (@$superClasses) {
	if (my $coderef = $super->can($super . "::handle")) {
	    last if ($wasHandled = $super->$coderef($msg));
	}
    }

    if (!$wasHandled && ref $self) {
	$self->trace( "Session $id has no handler for $type" );
	if ($type =~ /^-/) {	# Error
	    print "Session $id: error $type @rest\n";
	}
    }
    return $wasHandled;
}

sub settrace
{
    my $self = shift;
    my $was = defined $self->{_dotrace} ? $self->{_dotrace}
					: $self->{_connection}->settrace();
    if (@_) { $self->dotrace = shift }
    return $was;
}

sub trace
{
    my ($self, @rest) = @_;
    if (ref($self)) {
	my $dotrace = defined $self->{_dotrace}
		      ? $self->{do_trace}
		      : defined $self->{_connection}
			? $self->{_connection}->settrace()
			: 0;
	print @rest, "\n" if $dotrace;
    }
    else {
	Connection->trace( @_ );
    }
}

sub addhandler
{
    my ($self, $msg, $handler) = @_;

    if (ref $self) {
	# instance handler
	if (defined $self->{handlers}->{$msg}) {
	    $self->trace( "Session ".$self->id." replacing $msg hander" );
	}
	$self->{handlers}->{$msg} = $handler;
	print "instance handler for $msg established\n";
    }
    else {
	# class hander;
	no strict "refs";
	if (defined $self->{Handlers}->{$msg}) {
	    trace( "$self replacing $msg class hander" );
	}
	else {
	    trace( "$self class handler for $msg established" );
	}

	$self->{Handlers}->{$msg} = $handler;
    }
}

sub removehandler
{
    my ($self, $msg) = @_;

    if (ref $self) {
	# instance handler
	if (defined $self->{handlers}->{$msg}) {
	    $self->trace( "Session ".$self->id." removing $msg hander" );
	}
	delete $self->{handlers}->{$msg};
    }
    else {
	# class hander;
	no strict "refs";
	if (defined $self->{Handlers}->{$msg}) {
	    trace( "$self removing $msg class hander" );
	}

	delete $self->{Handlers}->{$msg};
    }
}

1;
