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

use Getopt::Long;
use Astro::Vex;
use Astro::Time;

$Astro::Time::StrSep = ':';
$Astro::Time::StrZero = 2;

use strict;

my $ant = 'Mp';
my $calscan = 1;
my $nofreq = 0;

GetOptions('cal!'=>\$calscan, 'antenna=s'=>\$ant, 'nofreq'=>\$nofreq);

if (@ARGV!=1) {
  die "Usage vex2sched.pl <vexfile>\n";
}

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

my %sources;
my %modes;

sub addsource ($$$$$$$);
sub addmode ($$$$$$);
sub modesetup ($$$\%);

my ($oldfreq1, $oldfreq2, $nfreq);

my $exper = $vex->exper->exper_name;
my $exper_lc = $exper;
$exper_lc =~ tr/A-Z/a-z/;
my $PIname = $vex->exper->pi_name;

my @scans = $vex->ant_sched($ant);
my $first = 1;
my $last_stop;

my $MAX_SCANS = 699 ;
my $OVERLAP_SCANS = 200 ;
my $schedfilenum = 0;
my $scans_end = 0; 
my $scans_start = 0; 

my $nchan ;
while ($scans_end == 0 || @scans > $scans_start+$MAX_SCANS) {
  my $first = 1;
  ++$schedfilenum;
  my $schedname ;
  my $atcaschedname ;
  if (@scans <= $MAX_SCANS) {
    $schedname = "${exper_lc}_Mp.com"; 
    $atcaschedname = $exper_lc ;
  }
  else {
    $schedname = "${exper_lc}${schedfilenum}_Mp.com";
    $atcaschedname = "${exper}${schedfilenum}" ;
  }
  warn "Warning: Overwriting exisiting schedule \"$schedname\"\n" if (-e $schedname);
  open(SCHED, '>', $schedname) || die "Could not open $schedname: $!\n";  

  $scans_start = $scans_end - $OVERLAP_SCANS if $scans_end > 0 ;
  $scans_end = $scans_start + $MAX_SCANS;
  $scans_end = $#scans if $#scans < $scans_end ;
  my @scans_this_file = @scans[$scans_start .. $scans_end] ;
  #print "nscans=", scalar(@scans), " scans_start=", $scans_start, " scans_end=", $scans_end, "\n";

  foreach (@scans_this_file) {
  
    my $source_def = $_->source;
    if ($source_def =~ /^\d+$/) {
      die "Do not support source names with digits only (".$source_def.")";
    }
    my $source = $vex->source($source_def);
    if (! defined $source) {
      die "Could not find source ".$_->source;
    }
    my $src = $source->source_name();
    if (! exists($sources{$src})) {
      $sources{$src} = {};
      $sources{$src}{RA} = $source->rastr;
      $sources{$src}{RA} =~ tr/hm/ /;
      $sources{$src}{RA} =~ s/s//;
      $sources{$src}{DEC} = $source->decstr;
      $sources{$src}{DEC} =~ tr/d\'/ /;
      $sources{$src}{DEC} =~ s/\"//;
      $sources{$src}{EPOCH} = $source->ref_coord_frame;
    }
  
    my $modename = $_->mode;
  
    # Groak this mode if we have not seen it before
    if (! exists $modes{$modename}) {
  
      $nchan = modesetup($vex, $ant, $modename, %modes);
  
    }
  
    my $scanlen = $_->stations($ant)->datastop;
    $scanlen->unit('day');  # Want scan length in days
    my $start = $_->start;
    my $stop = $start + $scanlen->value;
  
    my $freq1 = $modes{$modename}->{FREQ1};
    my $freq2 = $modes{$modename}->{FREQ2};
    my $bw = $modes{$modename}->{BANDWIDTH};
    my $scan = '';
    if ($first) {
      printf SCHED ("NCHAN=$nchan\n");
      $first=0;
      $oldfreq1 = $freq1;
      $oldfreq2 = $freq2;
      $nfreq = $modes{$modename}->{NFREQ};
  
      my $config = 'UNKNOWN';
      my $band1 = '64.0';
      my $band2 = '64.0';
  
      if ($ant eq 'At') {
  	$config = 'full_64_64_2_pol';
      } elsif ($ant eq 'Mp') {
        $config = 'ac_64_1024_2';
      }
  
      my $calsource = 'unknown';
      my $start = $_->start;
      $start += 5/60/60/24 if ($ant eq 'At');
  
      if ($calscan) {
        $start -= 1.5/24;  # Start 1.5 hour earlier
  
        if ($ant eq 'Mp') {
  	$calsource = '1057-797';
        } elsif ($ant eq 'At') {
  	my $lst = mjd2lst($start, str2turn('+149:33:00.5' ,'D'))*24;
  
  	if ($lst>11.5 || $lst<3.5) {
  	  $calsource="1934-638";
  	} else {
  	  $calsource="0823-500";
  	}
        }
      }
      my ($day, $month, $year, $ut) = mjd2cal($start);
      my @months = ('jan', 'feb', 'mar', 'apr', 'may', 'jun', 'jul',
  		  'aug', 'sep', 'oct', 'nov', 'dec');
      $month = $months[$month-1];
      $ut = turn2str($ut,'H',0);
      $start = "$day-$month-$year $ut";
  
      $freq1 = 'SETME' if ($freq1 == 0);
      $freq2 = 'SETME' if ($freq2 == 0);
  
      print SCHED<<EOF;
SCHED $atcaschedname
PROJ $exper
TIME UTC
OBS "$PIname"
UT $start
1FREQ $freq1
2FREQ $freq2
1BAND $band1
2BAND $band2
DUAL on
CONFIG $config
AVERAGING 1
EOF
    $scan = ($_->stop-$_->start)*24*60;

    $freq1 = undef;
    $freq2 = undef;

    if ($calscan) {
      print SCHED<<EOF;
CAT $calsource
SCAN 60m
ADD
EOF
        $scan += 30; # Start 30 minute early
      }
  
    } else {
      #if ($nfreq!=$modes{$modename}->{NFREQ}) {
      #  die "Does not support changing correlator config\n"
      #}
  
      if ($freq1==$oldfreq1) {
        $freq1 = undef;
      } else {
        $oldfreq1 = $freq1;
      }
      if (defined $freq2 && defined $oldfreq2 && $freq2==$oldfreq2) {
        $freq2 = undef;
      } else {
        $oldfreq2 = $freq2;
      }
  
      $scan = ($stop-$last_stop)*24*60;
    }
  
    addsource ($src, $sources{$src}{RA}, $sources{$src}{DEC},
  	     $sources{$src}{EPOCH}, $scan, $freq1, $freq2);
  
    $last_stop = $stop;
  }
}


sub addsource ($$$$$$$) {
  my ($source, $ra, $dec, $epoch, $scan, $freq1, $freq2) = @_;

  if (defined $freq1 && $freq1 == 0) {
    $freq1 = 'SETME';
  }
  if (defined $freq2 && $freq2 == 0) {
    $freq2 = 'SETME';
  }

  print SCHED "1FREQ $freq1\n" if (defined $freq1);
  print SCHED "2FREQ $freq2\n" if (defined $freq2);

  my $scanhour = 0;
  my $scanmin = $scan;
  while ($scanmin>=60) {
    $scanhour+=1;
    $scanmin-=60;
  }

  my $scanstr  = sprintf("%.6f", $scanmin);
  $scanstr =~ s/0+$//;
  $scanstr =~ s/\.$//;
  if ($scanhour>0) {
    $scanstr = "${scanhour}h$scanstr";
  }

  print SCHED<<EOF;
SOURCE $source
RA $ra
DEC $dec
EPOCH $epoch
SCAN ${scanstr}m
ADD
EOF
}

sub modesetup ($$$\%) {

  my ($vex, $ant, $modename, $modes) = @_;

  my $mode = $vex->mode($modename)->{$ant};

  my @chans = $mode->chan_def;
  my @chan_data;
  foreach (@chans) {
    my %vals = ();
    my $sideband = $_->sideband;
    my $sign;
    if ($sideband eq 'L') {
      $sideband = 'LSB';
      $sign = -1;
    } else {
      $sideband = 'USB';
      $sign = +1;
    }
    
    $vals{SIDEBAND} = $sideband;
    $vals{SIDESIGN} = $sign;

    my $pol = $_->pol;
    if ($pol eq 'R') {
      $pol = 'RCP';
    } elsif ($pol eq 'L') {
      $pol = 'LCP';
    }
    
    $vals{POL} = $pol;
    $vals{BW} = $_->bw->value;

    my $f0  = $_->freq;
    $f0->unit('MHz');

    my $f1 = $f0;
    if ($_->sideband eq 'U') {
      $f1 += $_->bw;
    } else {
      $f0 -= $_->bw;
    }
    $vals{F0} = $f0->value;
    $vals{F1} = $f1->value;

    push @chan_data, \%vals;
  }

  @chan_data = sort {$a->{F0} <=> $b->{F0}} @chan_data;

  my $freq1;
  my $freq2;
  my $nfreq;
  my $bw;
  my $nchan = scalar(@chan_data);
  # Sort out what sort of mode it is

  #printf SCHED ("NCHAN=$nchan\n");

  my $lsb=0;
  foreach (@chan_data) {
    if ($_->{SIDEBAND} eq 'LSB') {
      $lsb = 1;
      last;
    }
  }

  if ($nchan==0) {
    die "No frequency channels found for mode $mode\n";
  } elsif ($nchan==1) {
    # Will use VSOP profile

    if ($lsb) {
      warn "Does not support LSB in mode $modename\n";
      addmode($modes, $modename, 0, 0, 0, 0);
	return;
    }

    $nfreq = 1;
    $freq1 = $chan_data[0]->{F0}+$chan_data[0]->{BW};
    $freq2 = $freq1;
    $bw = $chan_data[0]->{BW};
  } elsif ($nchan==2) {

    if ($chan_data[0]->{F0}==$chan_data[1]->{F0}) {

      $nfreq = 1;
      $bw = $chan_data[0]->{BW};
      if ($bw==16) {
	# VSOP mode
	$freq1 = $chan_data[0]->{F0}+$chan_data[0]->{BW};

	if ($lsb) {
	  warn "Does not support LSB in mode $modename\n";
	  addmode($modes, $modename, 0, 0, 0, 0);
	  return;
	}
      } elsif ($chan_data[0]->{BW}==64) {
	$freq1 = ($chan_data[0]->{F0}+$chan_data[0]->{F1})/2;
      } else {
	$freq1 = $chan_data[0]->{F0}+$chan_data[0]->{BW}/2;
      }
      $freq2 = $freq1;

    } elsif ($chan_data[0]->{BW}==64 && $chan_data[1]->{BW}==64) {
      $nfreq = 2;
      $freq1 = $chan_data[0]->{F0}+$chan_data[0]->{BW}/2;
      $freq2 = $chan_data[1]->{F0}+$chan_data[1]->{BW}/2;
      #printf("This is MOPRA MODE\n");
      #exit(1);
    } elsif (($chan_data[0]->{F0}==$chan_data[1]->{F1} ||
	      $chan_data[0]->{F1}==$chan_data[1]->{F0})
	    ) {
      die "No not support single pol modes if BW != 16 MHz\n"
	if ($chan_data[0]->{BW}!=16 || $chan_data[1]->{BW}!=16);
      
      # VSOP mode
      $nfreq = 1;
      $freq1 = ($chan_data[0]->{F0}+$chan_data[1]->{F1})/2;
      $freq2 = $freq1;
    } else {
      $nfreq = 2;
      $freq1 = $chan_data[0]->{F0}+$chan_data[0]->{BW}/2;
      $freq2 = $chan_data[1]->{F0}+$chan_data[1]->{BW}/2;
    }
    $bw = $chan_data[0]->{BW};
    
  } elsif ($nchan==4) {
    # Check we understand this mode

    $bw = undef;
    for (my $i=0; $i<4; $i++) {
      if (!defined $bw) {
	$bw = $chan_data[$i]->{BW};
      } else {
	if ($bw != $chan_data[$i]->{BW}) {
	  die "Does not supports mixed bandwidths\n";
	}
      }
    }

    # Single pol??
    my $singlepol = 1;
    my $pol = $chan_data[0]->{POL};
    foreach (@chan_data) {
      if ($_->{POL} ne $pol) {
	$singlepol = 0;
	last;
      }
    }
    
    if ($singlepol) {
      die "Do not support 4 channel 64 MHz (mode $modename)" if ($bw==64);

      for (my $i=0; $i<4; $i+=2) {
	if ($chan_data[$i]->{F1}!=$chan_data[$i+1]->{F0}) {
	  die "Do not support this mode setup $modename";
	}
      }
      
      $nfreq = 2;
      $freq1 = ($chan_data[0]->{F0}+$chan_data[1]->{F1})/2;
      $freq2 = ($chan_data[2]->{F0}+$chan_data[3]->{F1})/2;

    } else {

      if ($bw!=16) {
	if ($chan_data[0]->{F0}!=$chan_data[1]->{F0} ||
	    $chan_data[2]->{F0}!=$chan_data[3]->{F0}) {
	  die "Do not support this mode setup $modename";
        }
	$nfreq = 2;
	$freq1 = ($chan_data[0]->{F0}+$chan_data[0]->{F1})/2;
	$freq2 = ($chan_data[2]->{F0}+$chan_data[2]->{F1})/2;
      } elsif ($chan_data[0]->{F0}==$chan_data[1]->{F0} &&
	       $chan_data[2]->{F0}==$chan_data[3]->{F0} &&
	       $chan_data[0]->{F0}+$bw==$chan_data[3]->{F0}) {
	# Standard dual pol
	$nfreq = 1;
	$freq1 = ($chan_data[0]->{F0}+$chan_data[2]->{F1})/2;
	$freq2 = $freq1;
      } elsif ($chan_data[0]->{F0}==$chan_data[1]->{F0} &&
	       $chan_data[2]->{F0}==$chan_data[3]->{F0}) {
	# Use VSOP profile - not sure this works with LSB
	$nfreq = 2;
	$freq1 = $chan_data[0]->{F1};
	$freq2 = $chan_data[2]->{F1};
      } elsif (($chan_data[0]->{F0}+$bw==$chan_data[1]->{F0}) &&
	       ($chan_data[2]->{F0}+$bw==$chan_data[3]->{F0})) {
	$nfreq = 2;
	$freq1 = $chan_data[0]->{F1};
	$freq2 = $chan_data[2]->{F1};
      } else {
	die "Do not support this mode setup $modename";
      }
    }
  } elsif ($nchan==8) {
    my $bw = undef;

    # Check not mixed bandwidth
    foreach (@chan_data) {
      if (!defined $bw) {
	$bw = $_->{BW};
      } else {
	if (($bw != $_->{BW})) {
	  my $bw2 = $_->{BW};
	  warn "Does not support mixed bandwith observations ($bw/$bw2)";
	  addmode($modes, $modename, 0, 0, 0, 0);
	  return;
	}
      }
    }


    

    # Check pairs of frequencies
    for (my $i=0; $i<4; $i++) {

      if (!$nofreq and ($chan_data[$i*2]->{F0}!=$chan_data[$i*2+1]->{F0} ||
	  $chan_data[$i*2]->{POL} eq $chan_data[$i*2+1]->{POL})) {
	die "Do not support this mode setup $modename";
      }
    }

    # Check 16 MHz pairs
    if (!$nofreq) {
      foreach my $i (0,4) {
	if ($chan_data[$i]->{F0}+$bw!=$chan_data[$i+2]->{F0}) {
	  die "Do not support this mode setup $modename";
	}
      }
    }
    
    $nfreq = 2;
    $freq1 = ($chan_data[0]->{F0}+$chan_data[2]->{F1})/2;
    $freq2 = ($chan_data[4]->{F0}+$chan_data[6]->{F1})/2;
    $bw = '16.0';
    
  } elsif ($nofreq) {
    $nfreq = 1;
    $freq1 = $freq2 = $chan_data[0]->{F0};  # Just choose approx freq
    $bw = 64.0;
  } else {
    die "Do not support $nchan channels in mode $modename\n";
  }

  addmode($modes, $modename, $freq1, $freq2, $nfreq, $bw);
  return $nchan ;
}

sub addmode ($$$$$$) {
  my ($modes, $modename, $freq1, $freq2, $nfreq, $bw) = @_;

  $modes->{$modename} = {};
  $modes->{$modename}->{FREQ1} = $freq1;
  $modes->{$modename}->{FREQ2} = $freq2;
  $modes->{$modename}->{NFREQ} = $nfreq;
  $modes->{$modename}->{BANDWIDTH} = $bw;
}
