#!/usr/bin/perl -w

use strict;
use LWP::UserAgent;
use Net::Ping;
use URI;
use Time::HiRes qw(usleep ualarm gettimeofday tv_interval);

$| = 1;

# list of URLs to find the mirror list
my @svr_list = ('http://envy/moo', 'file:server_list');

# file to download to test speed
my $test_file = '3/os/i386/GPL'; 

# file to get time/date from to gauge the mirror's freshness
my $age_file = '3/updates/i386/headers/header.info'; 

# seconds to wait for a mirror to respond to our request
my $timeout = 5; 

# HTTP useragent for this beast
my $user_agent_txt = 'Centos thingie for z00dax/0.1';

# how many tier 1 urls do we include in the return list
my $t1_max = 2;

###
my $DEBUG = 1;
my $top_n = 5;
my %mirror_list;
my %dead_mirror_list;
my $got_list = 0;
# force FTP into passive mode
$ENV{'FTP_PASSIVE'} = 1;

# let's get the list of mirror servers to check
foreach my $url (@svr_list)
{
  my $res = get_url($url);

  if($res->is_success)
  {
    my @m_list = split(/\n/, $res->content);

    foreach my $m (@m_list)
    {
      next if($m =~ /^#/);
      chomp($m);
      my ($_tier, $_url) = split(/\|/, $m);
      my $test_url = $_url . $test_file;
      my $duration = clock_url($test_url);

      if($duration)
      {
        $mirror_list{$_url} = { tier => $_tier, uri => URI->new($_url),
                                rtt => $duration }; 
#        print "-- Saving $_url : $_tier\n";
      }
      else
      {
#        print "** dead $_url : $_tier\n";
        $dead_mirror_list{$_url} = { error => $res->status_line };
        # do something about dead/slow mirrors here
      }
    }
    $got_list = 1;
    last;
  }
  else
  {
    print "Error getting mirror list from $url : " . $res->status_line . "\n" if($DEBUG);
    print "Trying next server in the list...\n" if($DEBUG);
  }
}

die "ERROR: Can't get a valid list of mirrors to check.\n" unless($got_list);

my @sorted = sort { $mirror_list{$a}->{rtt} <=> $mirror_list{$b}->{rtt} } keys(%mirror_list);

my $t1_count = 0;
my $total_count = 0;
my @top_n_list;

foreach my $_url (@sorted)
{
  # stop if we have $top_n number of mirrors
  last if($total_count == $top_n);
  # next if we have enough tier 1 mirrors
  next if($t1_count == $t1_max && $mirror_list{$_url}->{tier} == 1);
  
  $total_count++;
  $t1_count++ if($mirror_list{$_url}->{tier} == 1);
  push(@top_n_list, $_url); 
}

# take the $top_n fastest mirrors
#my @top_n_list = splice(@sorted, 0, $top_n);

foreach my $_url (@top_n_list)
{
  my $age_url = $_url . $age_file;
  my $res = get_url($age_url);

  if($res->is_success)
  {
    $mirror_list{$_url}->{age} = time() - $res->last_modified
  }

  print "$_url : $mirror_list{$_url}->{rtt} : $mirror_list{$_url}->{age} : $mirror_list{$_url}->{tier}\n"; 
}

sub clock_url
{
  my $url = shift;

  my $start = [gettimeofday];
  my $res = get_url($url, $timeout);
  my $duration = tv_interval($start); 
  
  return $res->is_success ? $duration : undef;
}

sub get_url
{
  my $url = shift;
  my $timeout = shift || 5;

  my $ua = LWP::UserAgent->new;
  $ua->agent($user_agent_txt);
  my $req = HTTP::Request->new(GET => $url);
  
  # $req->header('Accept' => 'text/html');
  # send request
  #my $res = $ua->request($req);
  #check the outcome
  $ua->timeout($timeout);
  return $ua->request($req);
}
