#!/usr/bin/perl -CDS

#
# Progress display for Tsukurimashou build system
# Copyright (C) 2011, 2012, 2020, 2021  Matthew Skala
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, version 3.
#
# As a special exception, if you create a document which uses this font, and
# embed this font or unaltered portions of this font into the document, this
# font does not by itself cause the resulting document to be covered by the
# GNU General Public License. This exception does not however invalidate any
# other reasons why the document might be covered by the GNU General Public
# License. If you modify this font, you may extend this exception to your
# version of the font, but you are not obligated to do so. If you do not
# wish to do so, delete this exception statement from your version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
# Matthew Skala
# http://ansuz.sooke.bc.ca/
# mskala@ansuz.sooke.bc.ca
#

# usage:
#  export PROGRESS_MESSAGE=text for the message
#  progress <command> <jobname> [<maxcount>]

($command,$jobname,$maxcount)=@ARGV;

$clreol=$maxcount;
$clreol=~s/\\e/\e/g;

$reftime=time;

$progprefix='PROGRESS:';
$progmsg=$ENV{'PROGRESS_MESSAGE'};

if ($progmsg=~/^(.*)@@@(.*)$/) {
  ($progprefix,$progmsg)=($1,$2);
  $progprefix=~s/\\e/\e/g;
}

sub format_seconds {
  my($rsec)=@_;
  my($rhr,$rmin);
  if ($rsec>=3600) {
    $rhr=int($rsec/3600);
    $rsec=$rsec-3600*$rhr;
    $rmin=int($rsec/60);
    $rsec=$rsec-60*$rmin;
    return sprintf("%d:%02d:%02d",$rhr,$rmin,int($rsec));
  } elsif ($rsec>=60) {
    $rmin=int($rsec/60);
    $rsec=$rsec-60*$rmin;
    return sprintf("%d:%02d",$rmin,int($rsec));
  } else {
    return int($rsec);
  }
}

sub process_update {
  $pct=100*($count/$maxcount);
  $pct=100 if $pct>100;
  $pct=0 if $pct<0;

  undef @estimates;

  $elapsed=$reftime-$times[0];
  push @estimates,$reftime+$elapsed*$maxcount/$count;

  $i=1;
  while ($i<$#counts-3) {
    $estimate=$times[$i]
      +($reftime-$times[$i])*($maxcount-$counts[$i])/($count-$counts[$i]);
    push @estimates,$estimate;
    $i=int((2*$i+$#counts)/3);
  }

  @estimates=sort @estimates;
  if ($#estimates>=4) {
    $nest=$#estimates+1;
    $low_cut=int(0.2*$nest);
    $high_cut=$#estimates-$low_cut;
    $low_est=$estimates[$low_cut];
    $high_est=$estimates[$high_cut];
  } else {
    $low_est=$estimates[0];
    $high_est=$estimates[$#estimates];
  }

  $low_est=int($low_est);
  $high_est=int($high_est);
  $low_est=$reftime if $low_est<$reftime;
  $high_est=$reftime if $high_est<$reftime;

  if ($count>=$maxcount) {
    printf "  %3d%%   %s %s/%s",
      int($pct),$progmsg,
      format_seconds($reftime-$starttime),
      format_seconds($reftime-$starttime);
  } elsif ($low_est==$high_est) {
    printf "  %3d%%   %s %s / %s (ETA %s)",
      int($pct),$progmsg,
      format_seconds($reftime-$starttime),
      format_seconds($low_est-$starttime),
      format_seconds($low_est-$reftime);
  } else {
    printf "  %3d%%   %s %s / %s-%s (ETA %s-%s)",
      int($pct),$progmsg,
      format_seconds($reftime-$starttime),
      format_seconds($low_est-$starttime),format_seconds($high_est-$starttime),
      format_seconds($low_est-$reftime),format_seconds($high_est-$reftime);
  }
}

@times=();
@counts=();

if ($command eq 'start') {
  open(DATA,">$jobname.prog");
  print DATA "0,$maxcount,$reftime\n";
  close(DATA);

} elsif ($command eq 'count') {
  open(DATA,"<$jobname.prog");
  while (<DATA>) {
    chomp;
    ($count,$maxcount,$thistime)=split(',');
    push @times,$thistime;
    push @counts,$count;
  }
  close(DATA);

  $starttime=$times[0];
 
  $count++;
  push @times,$reftime;
  push @counts,$count;

  open(DATA,">>$jobname.prog");
  print DATA "$count,$maxcount,$reftime\n";
  close(DATA);
  if ($reftime-$starttime<10) {
    exit(0);
  }

  print $progprefix;
  &process_update;
  print (($progprefix eq 'PROGRESS:')?"\n":"\r");

} elsif ($command eq 'stdin') {
  $|=1;
  $progmsg=$jobname;
  while (<STDIN>) {
    chomp;
    $reftime=time;

    if (/^PROGRESS START (\d+)$/) {
      $starttime=$reftime;
      $lastref=$reftime;
      $maxcount=$1;
      $count=0;
      push @times,$starttime;
      push @counts,$count;

    } elsif (/^PROGRESS ADD (\d+)$/) {
      $count+=$1;
      push @times,$reftime;
      push @counts,$count;
      if (($reftime-$lastref>=3) && ($reftime-$starttime>10)) {
        print $clreol;
        &process_update;
        print "\r";
        $lastref=$reftime;
      }
    }
  }
}
