#!/usr/bin/perl

use strict;
use warnings;

use Time::Local;
use Math::Amoeba qw(MinimiseND);

use Database;
my $dbh = Database->new->dbh;
use Wayside;

my ( $loncenter, $timecenter, $trainlength, $t0, $x0, $v0, $trainname ) = @ARGV;

my $argstr = join " ", @ARGV;

my $hitrev;
my @blameblockcum;

$loncenter  = int($loncenter);
$timecenter = int($timecenter);

my $intime0 = $t0;
my $intime9 = $t0 + 32;
my $width   = 20 * 180;

my $tc0a = $loncenter;

my $wayside =
  Wayside->new( $intime0, $loncenter - $width, $loncenter + $width );

my @columns = $wayside->xordered;

my $switches = $wayside->switch;

my $timesize;
my $timetofit = 32;

my @xs;

while (1) {
    my $t = $wayside->t;
    my $i = $t - $intime0;
    print "time is $t\n";
    my $x;
    for (@columns) {
	my $s = ${ $_->{state} };
	if (exists $switches->{$_->{pointname}}) {
	    print "there is a switchstate for ",$_->{pointname},"\n";
        	for ( @{ $switches->{$_->{pointname}} } ) {
                next if !$_->{used};
                my $switchstate = ${ $_->{switchstate} };
                my $leavedir    = $_->{leavedir};
            if ( 
                 $switchstate ne ( 3 - $leavedir )
		 && $s ne "0"
		)
            {
                $s = "R";
            }
	    }

	}
	if (exists $_->{landmark} && $_->{landmark} =~ /yard/i && $s ne "0") {
	    $s="R";
	}
        $x .= $s;
    }
    push @xs, $x;
    print "$i\t$t\t$x\n";
    last if !$wayside->updateto($intime9);
}

$timesize = scalar @xs;
print "timesize is $timesize\n";
my $datasize = scalar @columns;

my @xmfake;
my @xmreal;
my @fakedup;

for (@xs) {
    push @xmreal, [ split //, $_ ];
    push @xmfake, [ split //, $_ ];
}

my $gapfilled = 0;

#fill-in fake for time
for my $j ( 0 .. $datasize - 1 ) {
    my $i1;
    for my $i ( 0 .. $timesize - 1 ) {
        if ( $xmreal[$i][$j] eq "1" ) {
            if ( defined $i1 && $i - $i1 < 5 ) {
                for my $ii ( $i1 .. $i ) {
                    if ( $xmreal[$ii][$j] eq "0" ) {
                        $gapfilled++;
                        $xmfake[$ii][$j] = "1";
                        $fakedup[$ii]++;
                    }
                }
            }
            $i1 = $i;
        }
    }
}

print "gapfilled was $gapfilled\n";

my @bestguess;
my $bestscore;

my $finalx;

my %scorebykguess;

for my $a0d ( -10 .. 10 ) {
    my $a0 = $a0d * .5;
    for my $a16d ( -5 .. 5 ) {
        my $a16   = $a16d * 1;
        my @guess = ( $a0, $a0, $a16, $a16 );
        my $score = score(@guess);
        my $k     = int( $finalx / 500 );
        $scorebykguess{$k}{ join " ", @guess } = $score;
        print "Guess was ", ( join " ", @guess ),
          " and score was $score and k was $k\n";
    }
}

my @scale = ( 5, 5, 5, 5 );

for my $k ( keys %scorebykguess ) {
    my @guesses =
      sort { $scorebykguess{$k}{$a} <=> $scorebykguess{$k}{$b} }
      keys %{ $scorebykguess{$k} };
    my $guess = $guesses[0];
    print "score for k $k and $guess before amoeba was ",
      $scorebykguess{$k}{$guess}, "\n";
    my @guess = split / /, $guess;
    my ( $p, $y ) = MinimiseND( \@guess, \@scale, \&score, 0.01, 1000 );
    print "guess after amoeba is ", ( join " ", @$p ), "\n";
    print "score after amoeba is $y\n";
    if ( !defined $bestscore || $y < $bestscore ) {
        @bestguess = @$p;
        $bestscore = $y;
    }
}

print "final best guess is ", ( join " ", @bestguess ), "\n";

my @fit;
my $time0 = $intime0;
my $t     = $time0;
my $x     = $x0;
my $v     = $v0;
my $aa    = 0;
my $score = 0;
my $trklon0;
my $trklon9;
my $historybin;
my $lx;
$hitrev = 0;

my @as;
for (@bestguess) { push @as, $_, $_, $_, $_, $_, $_, $_, $_ }

@blameblockcum = (0) x $datasize;

for my $i ( 0 .. $timesize - 1 ) {
    $aa = shift @as;
    $v += $aa;
    $x += $v;
    if ( !$i ) {
        $historybin = pack( "NN", $t, int($x) );
        $lx = int($x);
    }
    else {
        my $delt = int( $x - $lx );
        $delt = 127  if $delt > 127;
        $delt = -127 if $delt < -127;
        $historybin .= pack( "c", $delt );
        $lx = $lx + $delt;
    }
    $trklon0 = $x if !defined $trklon0 || $x < $trklon0;
    $trklon9 = $x if !defined $trklon9 || $x > $trklon9;
    $score += vacancycost( $x, $xmreal[$i] );
    last if $hitrev;
    $t++;
}
$score = int($score);
my $time9 = $t - 1;
my $lth   = $dbh->prepare(
    q{
  INSERT INTO fits (time0,time9,trklon0,trklon9,width,fitbin,trainname,fitby) VALUES (?,?,?,?,?,?,?,?)
}
) or die $dbh->errstr;
$lth->execute( $time0, $time9, $trklon0, $trklon9, $trainlength, $historybin,
    $trainname, "measurebadness131" )
  or die $dbh->errstr;
my $fitnum = $dbh->last_insert_id( undef, undef, undef, undef );
my $newcolor = "red";
$newcolor = "orange"    if $score < 800;
$newcolor = "darkgreen" if $score < 100;
$dbh->do(
"UPDATE fitfails set badness=$score where trklon=$loncenter and time=$timecenter"
);
print
"update is UPDATE fitfails set badness=$score where trklon=$loncenter and time=$timecenter\n";
$dbh->do(
"UPDATE fitfails set fitnum=$fitnum where trklon=$loncenter and time=$timecenter"
);
print
"update is UPDATE fitfails set fitnum=$fitnum where trklon=$loncenter and time=$timecenter\n";
$dbh->do(
"UPDATE fitfails set color='$newcolor' where trklon=$loncenter and time=$timecenter"
);
my $mb = join " ", "./measurebadness132", @ARGV;
print
"update is UPDATE fitfails set measuredby=$mb where trklon=$loncenter and time=$timecenter\n";
$dbh->do(
"UPDATE fitfails set measuredby='$mb' where trklon=$loncenter and time=$timecenter"
);
my $j = 0;
my %blamenames;

for (@blameblockcum) {
    if ( $blameblockcum[$j] > 1 ) {
        print "block ", $columns[$j]->{pointname}, " gets blame ",
          $blameblockcum[$j], "\n";
        $blamenames{ $columns[$j]->{pointname} } += $blameblockcum[$j];
    }
    $j++;
}
my @blamenames;
my $bigblame;
for ( sort { $blamenames{$b} <=> $blamenames{$a} } keys %blamenames ) {
    last if defined $bigblame && $blamenames{$_} < $bigblame * 0.25;
    push @blamenames, $_;
    $bigblame = $blamenames{$_} if not defined $bigblame;
}
my $blameblock = join " ", @blamenames;
print
"update is UPDATE fitfails set blameblock=$blameblock where trklon=$loncenter and time=$timecenter\n";
$dbh->do(
"UPDATE fitfails set blameblock='$blameblock' where trklon=$loncenter and time=$timecenter"
);

print `./measurestale $argstr`;

sub score {
    my $score    = 0;
    my $physical = 0;
    my $x        = $x0;
    my $v        = $v0;
    $hitrev = 0;

    my @as;
    for (@_) { push @as, $_, $_, $_, $_, $_, $_, $_, $_ }

    @blameblockcum = (0) x $datasize;
    my @dumdum;

    for my $i ( 0 .. $timetofit - 1 ) {
        $a = shift @as;
        $v += $a;
        $x += $v;
        if ( $fakedup[$i] ) {
            my $vcfake = vacancycost( $x, $xmfake[$i] );
            @dumdum = @blameblockcum;
            my $vcreal = vacancycost( $x, $xmreal[$i] );
            @blameblockcum = @dumdum;
            $score += -0.04 * $vcreal + 1.04 * $vcfake;
        }
        else {
            $score += vacancycost( $x, $xmreal[$i] );
        }
        last if $hitrev;
        $physical += 0.1 * $a * $a;
        $physical += 1000 * ( abs($v) - 120 ) if ( abs($v) > 120 );
        $physical += 1000 * ( abs($a) - 3 )
          if ( abs($v) > 60 && $a * $v > 0 && abs($a) > 3 );
    }
    my @s = reverse sort @blameblockcum;
    shift @s;
    my $blamebonus = 0;
    for (@s) { $blamebonus += $_ * 4 }
    $finalx = $x;
    return $score + $physical + $blamebonus;
}

sub vacancycost {
    my ( $tl, $xrow ) = @_;
    my $tr = $tl + $trainlength;

    #  print "train between $tl and $tr\n";

    my $foundfeet = 0;

    for my $j ( 0 .. $datasize - 1 ) {
        my $tc  = $columns[$j];
        my $tcl = $tc->{trklon};
        my $tcr = $tc->{trklon} + $tc->{trklen};
        next if $tr < $tcl || $tl > $tcr;
        my $ff;
        if ( ( $tl <= $tcl ) && ( $tr >= $tcr ) ) {
            $ff = $tcr - $tcl;
        }
        elsif ( ( $tcl <= $tl ) && ( $tcr >= $tr ) ) {
            $ff = $tr - $tl;
        }
        elsif ( $tl <= $tcl && $tcl <= $tr && $tr < $tcr ) {
            $ff = $tr - $tcl;
        }
        elsif ( $tcl <= $tl && $tl <= $tcr && $tcr < $tr ) {
            $ff = $tcr - $tl;
        }
        if ( $xrow->[$j] eq "0" ) {
            $blameblockcum[$j] += $ff;
            next;
        }
        $foundfeet += $ff;
        $hitrev = 1 if $xrow->[$j] eq "R" && $ff;

        #    print "$xs tl $tl tr $tr tcl $tcl tcr $tcr foundfeet $foundfeet\n";
        #    print "foundfeet is now ",$foundfeet,"\n";
    }

    #  print $xs,"\t foundfeet:",$foundfeet,"\n";
    return ( $trainlength - $foundfeet );
}
