package Logfile::EPrints::Filter::Session;

use strict;

use vars qw( %SESSIONS $AUTOLOAD $TIDY_ON );

$TIDY_ON = 10000;

sub new
{
	my ($class,%self) = @_;
	$self{session} ||= 'Logfile::EPrints::Session';
	$self{_count} = 0;
	$self{_last_hit} = undef;
	bless \%self, ref($class) || $class;
}

sub AUTOLOAD
{
	return if $AUTOLOAD =~ /[A-Z]$/;
	$AUTOLOAD =~ s/^.*:://;
	my( $self, $hit ) = @_;
	my $address = $hit->address;
	if( defined($SESSIONS{$address}) and
		$SESSIONS{$address}->expired_by( $hit ) )
	{
		delete $SESSIONS{$address};
	}
	my $session = $SESSIONS{$address} ||=
		$self->{session}->new(
			filter => $self,
			address => $address,
		);
	$session->$AUTOLOAD( $hit );
	
	$self->_tidyup( $hit ) if ++$self->{_count} > $TIDY_ON;
	
	$hit->{session} = $session;
	return $self->{handler}->$AUTOLOAD($hit);
}

sub _tidyup
{
	my( $self, $hit ) = @_;
	$self->{_count} = 0;
	foreach my $address (keys %SESSIONS)
	{
		delete $SESSIONS{$address}
			if $SESSIONS{$address}->expired_by( $hit );
	}
}

package Logfile::EPrints::Session;

=head1 NAME

Logfile::EPrints::Session - Simple session class

=head1 METHODS

=over 4

=cut

use strict;
use warnings;

use vars qw( $AUTOLOAD $MAX_SESSION_GAP );

$MAX_SESSION_GAP = 60*10; # 10 minutes

sub new
{
	my( $class, %self ) = @_;
	bless \%self, $class;
}

=item $session->expired_by( $hit )

Returns true if this session would be expired before $hit occurred. NOTE for the purposes of tidyup $hit may not be from the same address as the session.

=cut

sub expired_by { ($_[1]->utime - $_[0]->{last_seen}) > $MAX_SESSION_GAP }

=item $session->start_session( $hit )

A new session has started with $hit.

=cut

sub start_session {}

=item $session->end_session

The session has expired/finished.

=cut

sub end_session {}

=item $session->total( [ $type ] )

Return the total number of requests in this session or, if $type is given, total requests for $type.

=cut

sub total
{
	my( $self, $type ) = @_;

	return @_ == 2 ?
		scalar keys %{$self->{requests}->{$type}} :
		$self->{requests}->{total};
}

sub DESTROY
{
	$_[0]->end_session;
}

sub AUTOLOAD
{
	return if $AUTOLOAD =~ /[A-Z]$/;
	$AUTOLOAD =~ s/^.*:://;
	my( $self, $hit ) = @_;

	if( !defined $self->{last_seen} )
	{
		$self->{first_seen} = $hit->utime;
		$self->start_session( $hit );
	}

	$self->{last_seen} = $hit->utime;
	
	$self->{requests}->{total}++;
	$self->{requests}->{$AUTOLOAD}->{$hit->identifier}++;
}

package Logfile::EPrints::Filter::MaxPerSession;

use strict;
use warnings;

our @ISA = qw( Logfile::EPrints::Filter );

use vars qw( $AUTOLOAD );

sub AUTOLOAD
{
	return if $AUTOLOAD =~ /[A-Z]$/;
	$AUTOLOAD =~ s/^.*:://;
	my( $self, $hit ) = @_;
	if( defined($self->{$AUTOLOAD}) and
		$hit->{session}->total($AUTOLOAD) > $self->{$AUTOLOAD} )
	{
		return undef if $hit->{session}->{__PACKAGE__ . '_removed'};
		$hit->{session}->{__PACKAGE__ . '_removed'} = 1;
		return Logfile::EPrints::Hit::Negate->new(
			address => $hit->address,
			start_utime => $hit->{session}->{first_seen},
			end_utime => $hit->{session}->{last_seen},
		);
	}
	else
	{
		return $self->{handler}->$AUTOLOAD( $hit );
	}
}

1;

__END__

=back
