package Net::Paraget::Server;
#
# $Id: Server.pm,v 1.8 2001/05/10 04:44:42 lrclause Exp $
#

use strict;

use Class::MethodMaker
    get_set       => [ qw( host scheme overhead list 
			   amount_completed work_time
			   total_download total_time
			   failures consecutive_failures
			   tries last_try
			 ) ],
    boolean       => [ qw( disabled ) ],
    list          => [ qw( mirrors ) ],
    new_hash_init => "hash_init",
    new_with_init => 'new';

my %serverdata_element_to_datamember =
  ( overhead               => "overhead",
    disabled               => "disabled",
    tries                  => "tries",
    failures               => "failures",
    "consecutive-failures" => "consecutive_failures",
    "total-download"       => "total_download",
    "total-time"           => "total_time",
    "last-try"             => "last_try",
  );


sub init
{
    my ($self, %args) = @_;

    for my $field ( qw( amount_completed work_time total_download total_time
			failures tries consecutive_failures overhead
		      ) ) 
    {
	$self->$field(0);
    }

    $self->hash_init(%args);

    die "Server list not initialized in init" if not $self->list();

    my $serverdata = $self->list()->serverdata($self->as_string());

    if ($serverdata)
    {
	foreach my $key ( keys %serverdata_element_to_datamember )
	{
	    my $function_name = $serverdata_element_to_datamember{$key};
	    my $total = $serverdata->{$key};
	    $self->$function_name($serverdata->{$key});
	}
	
	$self->hash_init(%args);
    }
    my $total = $self->total_download();
    my $consecutive_failures = $self->consecutive_failures();
    
    $self->state( "initted", 2 );
}

sub save_state
{
    my ($self) = @_;

    my $serverdata = {};

    foreach my $key ( keys %serverdata_element_to_datamember )
    {
	my $function_name = $serverdata_element_to_datamember{$key};
	if (defined $self->$function_name()) {
	    $serverdata->{$key} = $self->$function_name();
	}
    }

    return $serverdata;
}

sub speed
{
    my ( $self, $bad_arg ) = @_;
    die "You can't set speed" if defined $bad_arg;
    my $distance = $self->amount_completed();
    my $time     = $self->work_time();
    return undef unless $time;
    
    return int( $distance / $time );
}


sub as_string
{
    my ( $self ) = @_;
    return $self->host_scheme_as_string($self->host(), $self->scheme());
}


# this can be called as a class method
sub host_scheme_as_string
{
    my ($self, $host, $scheme) = @_;
    return lc join ( '', $scheme, '://', $host );
}

sub get_mirror
{
    my ( $self ) = @_;
    foreach ( $self->mirrors() )
    {
	return $_ unless $_->disabled();
    }
    return undef;
}


sub time_over_interval
{
    my ( $self, $interval ) = @_;
    
    my $prev = $interval->prev();
    
    my $prev_speed = 0;
    my $prev_assignment = $prev->assignment();
    $prev_speed = $prev_assignment->server->a_speed() if $prev_assignment;
    
    my $distance = $interval->size();
    my $overhead = $self->an_overhead();
    my $total_speed = $self->a_speed() + $prev_speed;
    
    my $time = $overhead + $distance / $total_speed;
    
    return $time;
}


sub get_a
{
    my ( $self, $value ) = @_;
    my $average_string = "average_$value";
    my $default_string = "default_$value";
    
    my $a = ( $self->$value()
	      || $self->list->$average_string()
	      || $self->list->$default_string()
	    );
    return $a;
}


sub a_speed
{
    my ( $self ) = @_;
    return $self->get_a( 'speed' );
}


sub an_overhead
{
    my ( $self ) = @_;
    return $self->get_a( 'overhead' );
}


sub id
{
    my ( $self ) = @_;
    $self->as_string();
}


sub info
{
    my ( $self ) = @_;
    return $self->id();
}


sub update_from_report
{
    my ( $self, $report ) = @_;

    if (not $self->amount_completed() and 
	$report->amount_completed())
    {
	# First piece of download, time up till now is overhead
	$self->overhead( $self->overhead() + $report->work_time );
    }

    foreach my $field ( qw( amount_completed work_time ) )
    {
	my $prev = $self->$field() || 0;
	$self->$field( $prev + $report->$field() );
    }
    
    $self->decrease_speed_history( 50 );
    
    my $speed = $self->speed();
    $speed = 'undefined' if not defined $speed;
}

# Must be called whenever a server starts downloading
sub at_start
{
    my ( $self ) = @_;
    $self->state( "starting", 1 );
    $self->tries( $self->tries() + 1 );
    $self->last_try( time() );
}

sub at_end
{
    my ( $self ) = @_;

    $self->total_time( $self->total_time + $self->work_time() );
    $self->total_download( $self->total_download + $self->amount_completed());
}

# Exactly one of these two must be called when a server stops downloading
sub at_success
{
    my ( $self ) = @_;

    $self->consecutive_failures(0);

    $self->at_end();
}

sub at_failure
{
    my ($self) = @_;

    $self->failures( $self->failures + 1 );
    $self->consecutive_failures( $self->consecutive_failures + 1 );

    $self->at_end();
}


sub count_able_mirrors
{
    my ( $self ) = @_;
    
    my $count = 0;
    foreach my $m ( $self->mirrors() )
    {
	
	$count++ if not $m->disabled();
    }
    return $count;
}


sub decrease_speed_history
{
    my ( $self, $decrease_to_percent ) = @_;
    
    my $original_completed = $self->amount_completed();
    my $original_time      = $self->work_time();
    
    my $multiplier = $decrease_to_percent / 100;
    
    my $new_completed = int( $original_completed * $multiplier );
    my $new_time      = int( $original_time * $multiplier );
    
    $self->amount_completed( $new_completed );
    $self->work_time( $new_time );
}


sub state
{
    my ( $self, $string, $priority ) = @_;
    main::state( $self->as_string . ": " . $string, $priority );
}


1;


syntax highlighted by Code2HTML, v. 0.9.1