#!/usr/local/bin/perl -w

use strict;

use Getopt::Long;
use Fcntl ':mode';

use Astro::Time;
use Astro::Vex;
use POSIX qw(ceil);
use MIME::Base64;
use Env;

sub dasmode ($$$$$$);
sub ftparea ($);
sub mjd2str ($);

$Astro::Time::StrZero = 2;

my $test=0;
my $overwrite = ();
my $singlesb = ();
#my $monica = ();

GetOptions('test'=>\$test, 'o'=>\$overwrite, 'single'=>\$singlesb);

if (@ARGV!=1) {
  usage();
}

my ($mpindex) = '';
my ($paindex) = '';
my ($atindex) = '';

my $vex = new Astro::Vex(shift);

if ((!defined $vex)) {
  die "Error reading vexfile\n";
}

my $experiment_name = lc($vex->exper->exper_name);
my $PIname = $vex->exper->pi_name;
my $description = $vex->exper->exper_description;

printf STDERR "Making wiki for $experiment_name\n";

my @scans = $vex->sched;

# Find which scan this time corresponds to. Assume that the scan list
# is in order

my ($sourcename);

my %modes;

# Determine unique modes and scan start/stop times

my $sched_start = $scans[0]->start;
my $sched_stop = $scans[0]->stop;
#my %tape_changes = ();

my %ant_lastpos = ();
my $laststop;

# changemode will list the name and time of any mode changes.
my @changemode;
$changemode[0] = [$scans[0]->mode, $scans[0]->start];

foreach (@scans) {

  $modes{$_->mode} = 1;

  # record the name and time of any mode changes
  if ($_->mode ne $changemode[$#changemode][0]) {
    push @changemode, [$_->mode, $_->start];
  }

  $sched_start = $_->start if ($_->start<$sched_start);
  $sched_stop = $_->stop if ($_->stop>$sched_stop);

  my $scan = $_->scanid;
  my @ants = $_->stations();
  foreach (@ants) {
    my $ant = $_->station;
    my $thispos = $_->startpos;
    #if (exists $ant_lastpos{$ant}) {
    #  if ($thispos<$ant_lastpos{$ant}) {
#	$tape_changes{$laststop} = 1; # Only record each uniq tapechange
    #  }
    #}

    $ant_lastpos{$ant} = $_->startpos;
  }
  $laststop = $_->stop;
}
#my @tapechanges = sort {$a <=> $b} (keys(%tape_changes));

my ($das_mode, @notes, $bandwidth);
#my @freqs;

if (keys(%modes)==0) {
  print "No scans found\n";
  exit(0);
}

my @stations = $vex->stationlist;

my $modename = (keys %modes)[0];

#my $obsmode = undef;

my $start = mjd2str($sched_start);
my $stop = mjd2str($sched_stop);

my ($day, $month, $year, $ut) = mjd2cal($sched_start);
my ($start_dayno, $start_year, $start_ut) = mjd2dayno($sched_start);
my ($stop_dayno, $stop_year, $stop_ut) = mjd2dayno($sched_stop);

$start .= " ($day/$month/$year)";

my $session = sprintf("%s%d", month2str($month), $year);
my $ut_start_str = turn2str($start_ut, 'H', 0);
my $ut_stop_str = turn2str($stop_ut, 'H', 0);

my $outname;

# get a list of the lba stations

my $ants = join '-', @stations;

if (!$test) {
  my ($outfile)  = lc("lbaops:lba$session:$experiment_name");
  print "Wiki page: $outfile\n";
  open(my $wikiin, "<$ENV{'HOME'}/.wikiin");
  my ($stuff) = quotemeta(decode_base64(<$wikiin>));
  $outname = "postwiki.py -o -u vlbi -p $stuff $outfile";
  open STDOUT, '|-', "$outname" || die "Can't redirect STDOUT to $outname: $!";
}

print_description();

# loop through all the modes printing the wiki output for each.
for $modename (keys %modes){

  print<<EOF

Setup $modename:
EOF
  ;

  # Group the LBA stations that are set up identically in this mode.


  # compare the setup of each station with the others to see if they match.
  my (@station_group) = ();
  foreach my $id (@stations) {
    my $knownmode = ();
    my @stationmode = ();
    next if (! exists($vex->mode($modename)->{$id}));
    foreach my $channel ($vex->mode($modename)->{$id}->chan_def) {
      push @stationmode, [$channel->bbc, $channel->freq, $channel->pol];
    }
    my ($recorder) = $vex->mode($modename)->{$id}->record_transport_type;

    # compare this recorder and frequency setup to existing known ones
    foreach my $group (@station_group) {
      if ($recorder eq $group->{'recorder'}) {
        if (compare_mode(\@stationmode, \@{$group->{'mode'}})) {
          push @{$group->{'stations'}}, $id;
          $knownmode = 1;
        }
      }
    }
    if (!$knownmode) {
      # this is the first station with this recorder/frequency setup
      my ($newgroup) = $#station_group + 1;
      push @{$station_group[$newgroup]{'stations'}}, $id;
      push @{$station_group[$newgroup]{'mode'}}, @stationmode;
      $station_group[$newgroup]{'recorder'} = $recorder;
    }
  }

  my $found = 0;
  my $ndas = ();
  my $recorder = ();


  # Print mode info for each group of stations.
  foreach my $group (@station_group) {

    my @skyfreq = ();

    # get the mode of a representative station from this group
    my ($id) = $group->{'stations'}[0];
    my $mode = $vex->mode($modename)->{$id};

    $found = 1;


    my @chans = $mode->chan_def;

    my (@freqs) = ();

    # Work out DAS mode and buggary
    $das_mode = undef;
    $bandwidth = undef;
    my (%bbcinfo) = ();

    if (@chans==0) {
      warn "No data channels found!!!\n";
    }else {

      # get a list of the IFPOLs (= BBC) in use and their frequencies and pols.
      # freq and sideband are lists whose ordering should always match.
      foreach my $channel (@chans) {
        my ($freq) = $channel->freq;
        $freq =~ s/\s*MHz\s*//;
        push @{$bbcinfo{$channel->bbc}{'freq'}}, $freq;
        push @{$bbcinfo{$channel->bbc}{'sb'}}, $channel->sideband;
        $bbcinfo{$channel->bbc}{'pol'} = uc($channel->pol . 'CP');
        my ($bw) = $channel->bw;
        $bw =~ s/\s*MHz\s*//;
        $bbcinfo{$channel->bbc}{'bw'} = $bw ;
      }

      # extract a few useful (global) values 
      my ($a_bbc) = (keys %bbcinfo)[0];
      my ($nsb) = scalar(@{$bbcinfo{$a_bbc}{'sb'}});
      my ($bw) = $bbcinfo{$a_bbc}{'bw'};

      # sort the frequencies on each BBC (keeping the sideband list in same
      # order).
      if ($nsb > 1) {
        foreach my $bbc (keys %bbcinfo) {
          my (@sort_index) = sort {$bbcinfo{$bbc}{'freq'}[$a] <=> $bbcinfo{$bbc}{'freq'}[$b]} (0,1);
          @{$bbcinfo{$bbc}{'freq'}} = @{$bbcinfo{$bbc}{'freq'}}[@sort_index];
          @{$bbcinfo{$bbc}{'sb'}} = @{$bbcinfo{$bbc}{'sb'}}[@sort_index];
        }
      }


      # Then calculate central frequencies of each IFP (BBC).
      foreach my $bbc (keys %bbcinfo) {
        my ($bbc_lowfreq) = ();
        my ($bbc_hifreq) = ();
        for (my $i=0; $i<@{$bbcinfo{$bbc}{'freq'}}; ++$i) {
          my ($sb) = $bbcinfo{$bbc}{'sb'}[$i];
          my ($freq) = $bbcinfo{$bbc}{'freq'}[$i];
          my ($lowfreq) = $freq*1;
          my ($hifreq) = $lowfreq + $bw * sbsign($sb);
          ($lowfreq, $hifreq) = sort {$a<=>$b} ($lowfreq, $hifreq);
          $bbc_lowfreq = $lowfreq if (!$bbc_lowfreq || $lowfreq < $bbc_lowfreq);
          $bbc_hifreq = $hifreq if (!$bbc_hifreq || $hifreq > $bbc_hifreq);
        }
        push @skyfreq, ($bbc_lowfreq + $bbc_hifreq)/2;
      }
      @skyfreq = unique(@skyfreq);
      @skyfreq = sort {$a<=>$b} (@skyfreq);

      my $midfreq = $skyfreq[int scalar(@skyfreq)/2];


      $ndas = ceil(scalar(keys %bbcinfo)/2);
      my ($nif_per_das) = scalar(@skyfreq)*$nsb/$ndas ;

      $recorder = $vex->mode($modename)->{$id}->record_transport_type;

      # for 16 MHz bw, force dual sideband (vsop.pro) modes, unless told
      # otherwise
      if ($recorder =~ /S2/ || $recorder =~ /LBA/ ) {
        if ($nif_per_das == 1 && $bw == 16 && !$singlesb) {
          $nif_per_das = 2;
          foreach my $freq (@skyfreq) {
            $freq += $bw/2.
          }
          push @notes, "@{$group->{'stations'}}: Note the use of the dual sideband vsop profile. Only the lower sideband should be selected for transfer.";
        }
      }

      # check if we have dual usb or conventional lsb/usb pairs
      my ($dualusb) = ();
      my (@bbc_sb) = @{$bbcinfo{$a_bbc}{'sb'}};
      if (scalar(@bbc_sb) > 1) {
        if ($bbc_sb[0] eq $bbc_sb[1]) {
          $dualusb = 1;
        }
      }

      # calculate the DAS mode using the freq info from a random IFPOL.
      $das_mode = dasmode( $bbcinfo{$a_bbc}{'bw'}, $nif_per_das,
        $bbcinfo{$a_bbc}{'freq'}[0] + $bbcinfo{$a_bbc}{'bw'}/2., $group,
        $recorder, $dualusb);

      # Create a sorted list of the channels with their freq information.
      my ($i_ifp) = 0;
      my ($i_das) = 1;
      foreach my $bbc (sort {by_freq_pol(\%bbcinfo)} keys %bbcinfo) {
        ++$i_ifp;
        ++$i_das    if ($i_ifp/$i_das > 2);
        my ($i_sb) = 0;
        my @bbcfreq = ();
        foreach my $freq (@{$bbcinfo{$bbc}{'freq'}}) {
          my ($pol) = $bbcinfo{$bbc}{'pol'};
          my ($sb) = $bbcinfo{$bbc}{'sb'}[$i_sb] . 'SB';
          my ($lofreq) = $freq*1;
          my ($hifreq) = $lofreq + $bw * sbsign($sb);
          ($lofreq, $hifreq) = sort {$a<=>$b} ($lofreq, $hifreq);
          my ($das) = '';
          my ($ifpol) = '';
          if ($recorder =~ /S2/ || $recorder =~ /LBA/) {
            $das = "DAS #" . $i_das    if ($ndas>1);
            $ifpol = 'IFP#' . sprintf("%1d", 2-$i_ifp%2);
            if ($nsb>1 ) {
              if (!$i_sb%2) {
                $ifpol .= '-LO'    ;
              }else {
                $ifpol .= '-HI'    ;
              }
            }
          }
          push @freqs, "$das $ifpol $lofreq - $hifreq MHz $sb $pol";
          push @bbcfreq, $lofreq;
          $bandwidth=$bw*1;
          ++$i_sb;
        }
      }
    }


    if (!$found) {
      print " Did not find any stations with S2 transports\n";
    }


    print "\n^Station Modes                 | @{$group->{'stations'}} |\n";

    my $c=1;
    foreach (@freqs) {
      if (/not used/) {
        printf "^                   | $_ |\n";
      } else {
        printf "^Channel $c          | ''$_'' |\n";
      }
      $c++;
    }

    my ($chanperdas) = scalar(@skyfreq)/$ndas;
    my ($i_skyfreq) = 0;
    if ($recorder =~ /S2/ || $recorder =~ /LBA/){
      for (my $d=0; $d<$ndas; ++$d) {
        my ($dasnum) = $d+1;
        my ($skyfreq) = ();
        for (my $i=0; $i<$chanperdas; ++$i) {
          if ($skyfreq) { 
            $skyfreq .= ' & '
          }
          $skyfreq .= $skyfreq[$i_skyfreq] . ' ';
          ++$i_skyfreq;
        }
        printf "^DAS $dasnum Skyfreq      | $skyfreq MHz |\n";
      }
    }

    # and the index line for the antab files
    foreach my $station (@{$group->{'stations'}}) {
      if ($station eq 'Mp') {
        $mpindex = getindex(\%bbcinfo) 
      }
      if ($station eq 'Pa') {
        $paindex = getindex(\%bbcinfo) 
      }
      if ($station eq 'At') {
        $atindex = getindex(\%bbcinfo) 
      }
    }


  print<<EOF;
^Bandwidth          | $bandwidth MHz         |
^DAS Mode           | $das_mode |
EOF
;

  }

}

if (@changemode > 1) {
  print "\n**Mode changes:**\\\\\n";
  for my $newmode (@changemode) {
    print mjd2str($newmode->[1]), " ", $newmode->[0], "\\\\\n";
  }
}

my $ftparea = ftparea($experiment_name);
print "\nFtp: [[ftp://ftp.atnf.csiro.au/pub/people/vlbi/$ftparea]]\n";

print<<EOF;
----

==== Comments: ====

EOF

foreach (@notes) {
  printf "$_\n\n";
}

# Add a section for observatory comments


print<<EOF;
==== Observing comments for each antenna: ====
EOF
;

foreach (@stations) {
  my $link  = lc("${experiment_name}${_}log");
  print "| [[ $link | $_]] ";
}
print "|\n";

# Add a section for logs (Tsys, on-source, weather)

print<<EOF;

----

==== Observing Logs ====
[[http://www.atnf.csiro.au/cgi-bin/vlbi/atca_summary.pl?start=$start_dayno/$ut_start_str&end=$stop_dayno/$ut_stop_str&year=$year&site=ATCA| ATCA antenna summary]]\\\\
[[http://www.atnf.csiro.au/cgi-bin/vlbi/parkes-onsourcenew.pl?year=$year&start=$start_dayno/$ut_start_str&end=$stop_dayno/$ut_stop_str| Parkes onsource flagging]]\\\\
[[http://www.atnf.csiro.au/cgi-bin/vlbi/atca-onsource.pl?start=$start_dayno/$ut_start_str&end=$stop_dayno/$ut_stop_str&year=$year| ATCA onsource flagging]]\\\\
[[http://www.atnf.csiro.au/cgi-bin/vlbi/mopra_onsource.pl?exper=$experiment_name| Mopra onsource flagging]]\\\\
[[http://www.atnf.csiro.au/cgi-bin/vlbi/mopra_tsys.pl?exper=$experiment_name&plot=0| Mopra Tsys]]
([[http://www.atnf.csiro.au/cgi-bin/vlbi/mopra_tsys.pl?exper=$experiment_name&plot=1|plot]])\\\\
[[http://www.atnf.csiro.au/cgi-bin/vlbi/parkes_tsys.pl?year=$year&start=$start_dayno/$ut_start_str&end=$stop_dayno/$ut_stop_str| Parkes Tsys]]\\\\

==== Weather ====
[[http://www.atnf.csiro.au/cgi-bin/vlbi/monica_weather.pl?start=$start_dayno/$ut_start_str&end=$stop_dayno/$ut_stop_str&year=$year&site=ATCA| ATCA Weather]]\\\\
[[http://www.atnf.csiro.au/cgi-bin/vlbi/monica_weather.pl?start=$start_dayno/$ut_start_str&end=$stop_dayno/$ut_stop_str&year=$year&site=Mopra| Mopra Weather]]\\\\
[[http://www.atnf.csiro.au/cgi-bin/vlbi/monica_weather.pl?start=$start_dayno/$ut_start_str&end=$stop_dayno/$ut_stop_str&year=$year&site=Parkes| Parkes Weather]]\\\\

EOF
;

print<<EOF;
==== Monica log information  ====
[[http://www.atnf.csiro.au/cgi-bin/vlbi/monica_tsys.pl?year=$year&start=$start_dayno/$ut_start_str&end=$stop_dayno/$ut_stop_str&index=$mpindex&tel=Mp | Mopra Tsys]]\\\\
[[http://www.atnf.csiro.au/cgi-bin/vlbi/monica_tsys.pl?year=$year&start=$start_dayno/$ut_start_str&end=$stop_dayno/$ut_stop_str&index=$mpindex&tel=Pa | Parkes Tsys]]\\\\
[[http://www.atnf.csiro.au/cgi-bin/vlbi/monica_tsys.pl?year=$year&start=$start_dayno/$ut_start_str&end=$stop_dayno/$ut_stop_str&index=$atindex&tel=At | ATCA Tsys]]\\\\
[[http://www.atnf.csiro.au/cgi-bin/vlbi/atnf_clocks.pl?year=$year&start=$start_dayno/$ut_start_str&ndays=2 | ATNF Clock Offsets]]
EOF
;

if (!$test) {
  chmod S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH, $outname;
}

sub mjd2str ($) {
  my $mjd = shift;

  my ($dayno, $year, $ut) = mjd2dayno($mjd);
  $ut = turn2str($ut, 'H',0);
  return "$dayno $ut";
}

sub dasmode ($$$$$$) {
  my ($bw, $nif, $freq, $group, $recorder, $dualusb ) = @_;

  if (!($recorder =~ /S2/) && !($recorder =~ /LBA/)) {
    $das_mode = 'Mark5';
    return $das_mode;
  }

  #print "NIF=$nif\n";

  my $mode = '??';
  my $page = '??';

  if ($nif==1) {
    if ($bw == 16) {
      $mode = 'at16s.pro';
      $page = '??';
    } elsif ($bw == 32) {
      $page = '32MHz';
      $mode = 'mp32_[nf].pro';
    } elsif ($bw == 8) {
      $page = '??';
      $mode = 'at8s.pro';
    } elsif ($bw == 4) {
      $page = '?';
      $mode = 'at4s.pro';
    } elsif ($bw == 64) {
      $mode = '64MHz_[nf].pro';
      $page = '64MHz';
    } else {
      return '??';
    }
  } elsif ($nif==2) {
    if ($bw == 16) {
      if ($dualusb) {
        $mode = 'vsop.pro';
        $page = 'VSOP';
      } else{
        $mode = '16mhz_ul';
        $page = '16MHz_ul';
      }
    } elsif ($bw == 8) {
      $mode = "Special";
    } elsif ($bw == 32) {
      $page = '32MHz';
      $mode = 'mp32_[nf].pro';
    } elsif ($bw == 4) {
      return 'Need to do 4 MHz setup';
    } elsif ($bw == 64) {
      $page = '64MHz';
      $mode = '64MHz_n.pro';
    } else {
      return '??';
    }
  } elsif ($nif==4) {
    if ($bw == 16) {
      if ($dualusb) {
        $mode = 'vsop.pro';
        $page = 'VSOP';
      } else{
        $mode = '16mhz_ul';
        $page = '16MHz_ul';
      }
    } elsif ($bw == 64) {
      $page = '64MHz';
      $mode = '64Mhz.pro';
      push @notes, "@{$group->{'stations'}}: Dual frequency setup required. Will need VSIC setup and 2 recorders";
    } else {
      return '??';
    }
  }

  return "$mode ([[http://www.evlbi.atnf.csiro.au/daspro/daspro.php?$page=$freq|telescope]])";

}

sub ftparea ($) {
  my $expr = shift;
  my $parentdir = '';

  if ($expr =~ /(^.*\d+)[^\d].*$/) {
    $parentdir = "$1";
  }

  if ($expr =~ /(^vt02\D)\S/i) {
    $parentdir .= "/$1";
  } elsif ($expr =~ /^vc\d+/i) {
    $parentdir = "fringe-checks";
    $expr =~ s/[a-z]$//;
  } elsif ($expr =~ /^re\d+/i 
        || $expr =~ /^rk\d+/i
        || $expr =~ /^bk\d+/i
        || $expr =~ /^rg\d+/i
          ) {
    $parentdir = "radioastron";
  } elsif ($expr =~ /^c\d\d\d\D/i) {
    $parentdir = 'gmva';
  }

  return "${parentdir}/$expr";
}

sub sbsign {
  # convert U/L sideband to 1/-1
  my ($sb) = $_[0];
  my ($sbsign) = ();
  if ($sb =~ /U/i) {
    $sbsign = 1;
  }elsif ($sb =~ /L/i) {
    $sbsign = -1;
  }else {
    warn "Unrecognised Sideband";
  }
  return $sbsign;
}

sub unique {
  # remove duplicate elements from a list.
  my (@list) = @_;
  my (%seen,@unique,$item) = ();

  foreach $item (@list) {
    push (@unique, $item)      unless $seen{$item}++ ;
  }

  return @unique;
}

#sub compmode {
#  # compare two telescope setups to see if they are identical
#  # assume if number of channels are the same then they are identical
#
#  my ($mode1, $mode2) = @_;

sub print_description {
  print<<EOF;
==== $experiment_name ====
^Description        | $description |
^Antennas           | $ants |
^Start              | $start   |
^Stop               | $stop   |
^PI                 | $PIname |

EOF
;
}

sub by_freq_pol {
  # sort by the frequency, then polarization of the BBC. (RCP before LCP).
  my %bbcinfo = %{$_[0]};
  $bbcinfo{$a}{'freq'}[0] <=> $bbcinfo{$b}{'freq'}[0] 
  || $bbcinfo{$b}{'pol'} cmp $bbcinfo{$a}{'pol'} ;
}


sub compare_mode {
  # compare two station modes to see if they are identical
  my (@st1) = @{$_[0]};
  my (@st2) = @{$_[1]};

  my ($ident) = 1;

  if (scalar @st1 != scalar @st2) {
    # check number of channels
    $ident = 0;
  }else {
    # check the frequency setup matches
    for (my $i=0; $i<@st1; ++$i) {
      # check bbc name
      if ($st1[$i][0] ne $st2[$i][0]) {
        $ident = 0;
      }
      # check bbc frequency
      if ($st1[$i][1] != $st2[$i][1]) {
        $ident = 0;
      }
      # check bbc polarization
      if ($st1[$i][2] ne $st2[$i][2]) {
        $ident = 0;
      }
    }
  }

  return $ident;
}

sub usage {
print <<EOF

Usage: makewiki.pl [-test -o -single] <vexfile>

options:
-test   does not write output directly to the wiki directory
-o      overwrite existing wiki file without prompting
-single do not force the use of dual sideband modes where single sideband
        are possible
-monica create links for monica tsys
EOF
;
exit
}

sub getindex {
  # calculate the ANTAB index line for a setup
  my (%mode) = %{$_[0]};

  my (%index) = ('BBC01' => "'X'", 'BBC02' => "'X'", 'BBC03' => "'X'",
    'BBC04' => "'X'" );

  my (%polnum) = ();
  foreach my $bbc (sort keys %mode) {
    my ($pol) = $mode{$bbc}{'pol'} ;
    $pol =~ s/CP$//;
    if (!defined $polnum{$pol}) {
      $polnum{$pol} = 0;
    }
    ++$polnum{$pol};
    $index{$bbc} = "'" . $pol . $polnum{$pol};
    if (@{$mode{$bbc}{'sb'}} > 1) {
      ++$polnum{$pol};
      $index{$bbc} .= ':' . $polnum{$pol};
    }
    $index{$bbc} .= "'";
  }

  my ($index) = "";
  foreach my $bbc (sort keys %index) {
    $index .= $index{$bbc} . ',';
  }
  $index =~ s/,$//;

  return $index;
}
