#!/usr/bin/env perl

use strict;
use warnings;

use Getopt::Long;
use Pod::Usage;
use FileHandle;

use PS::MOPS::Constants qw(:all);
use PS::MOPS::DX;
use PS::MOPS::DC::Tracklet;

use subs qw(
    _out_track
);


my $inst;
my $instance_name;
my $twopass;
my $tracklets;
my $oid = 'A000000';                # OID for attr/precov
my $help;
GetOptions(
    'instance=s' => \$instance_name,
    twopass => \$twopass,
    tracklets => \$tracklets,
    'oid=s' => \$oid,               # hard-coded OID for single-orbit evals (attribution/precovery)
    help => \$help,
) or pod2usage(2);
pod2usage(-verbose => 2) if $help;
$inst = PS::MOPS::DC::Instance->new(DBNAME => $instance_name);

my $in_filename;
my $out_fileroot;
my %printed_tracklet_ids;      # prevent duplicate output of tracklets to in.tracklet file

$in_filename = (shift or '-');      # filename from command-line or use stdin
if ($in_filename eq '-') {
    $out_fileroot = 'stdin';        # looks like stdin
}
else {
    $out_fileroot = $in_filename;   # copy input filename
    $out_fileroot =~ s/\.sum$//g;   # strip '.sum' suffix if present
}

# File names and handles.
my ($tracklet_filename, $request_filename, $manifest_filename, $opt_filename);
my ($tracklet_fh, $request_fh, $manifest_fh, $opt_fh);

if ($twopass) {
    my $job_dir = "$out_fileroot";

    system("/bin/mkdir -p $job_dir/pass1") == 0 or die "can't create $job_dir/pass1";
    system("/bin/mkdir -p $job_dir/pass2") == 0 or die "can't create $job_dir/pass2";

    # We need to generate two options files, one for each pass.
    $opt_fh = new FileHandle ">$job_dir/pass1/$out_fileroot.opt" or die "can't create options file $job_dir/pass1/$out_fileroot.opt";
    print $opt_fh <<"OPT";
! Machine generated by $0.
orbsrv.
         .inpdir='.'                ! input directory
         .outdir='.'                ! output directory
!         .output_unidentif= .T.     ! output unidentified tracklets (def. F)
         .force_difcor= .T.         
!        .cooy='COM'                ! output cometary orbital elements (COM); or COT (cometary true anomaly)
!
OPT
    close $opt_fh;
    print STDERR "Wrote $out_fileroot.opt.\n";

    $opt_fh = new FileHandle ">$job_dir/pass2/${out_fileroot}_2.opt" or die "can't create options file $job_dir/pass2/${out_fileroot}_2.opt";
    print $opt_fh <<"OPT";
! Machine generated by $0.
orbsrv.
         .inpdir='.'                ! input directory
         .outdir='.'                ! output directory
         .force_difcor= .T.         ! diff cor even if REQUEST_PRELIM (def F)
!         .output_unidentif= .T.     ! output unidentified tracklets (def. F)
         .skip_misstra=.T.          ! skip track with missing tracklets (def. F)
         .prelim_rms=   1000.d0     ! output prelim if RMS<this (def 100)
         .cooy='COM'                ! output cometary orbital elements (COM); or COT (cometary true anomaly)
OPT
    close $opt_fh;
    print STDERR "Wrote ${out_fileroot}_2.opt.\n";

    $tracklet_filename = "$job_dir/pass1/$out_fileroot.in.tracklet";
    $request_filename = "$job_dir/pass1/$out_fileroot.in.request";
    $manifest_filename = "$job_dir/pass1/$out_fileroot.in.manifest";
}
else {
    # Create options file.
    $opt_fh = new FileHandle ">$out_fileroot.opt" or die "can't create options file $out_fileroot.opt";
    print $opt_fh <<"OPT";
! Machine generated by $0.
orbsrv.
! slow iteration of orbit_server with input tracks from MOPS code
     .force_difcor=.T.       ! compute least sq. orbit
!     .inpdir='.'            ! input directory (def .)
!     .outdir='.'            ! output directory (def .)
     .cooy='COM'             ! output cometary orbital elements (COM)
! logical controls of prelim_orbit
   .smart_ecclim_cont=0.1d0  ! eta max for hyperbolics, deg/d (def. 0)
   .smart_gaussmap=.T.       ! use elongation to select Gauss map (def F)
! control parameters for prelim_orbit
     .prelim_rms=  1000.d0 ! output prelim if RMS<this (def 100)
OPT
    close $opt_fh;
    print STDERR "Wrote $out_fileroot.opt.\n";

    $tracklet_filename = "$out_fileroot.in.tracklet";
    $request_filename = "$out_fileroot.in.request";
    $manifest_filename = "$out_fileroot.in.manifest";
}

# Create file handles for tracklets and request.
$tracklet_fh = new FileHandle ">$tracklet_filename" or die "can't create tracklet file $tracklet_filename";
$request_fh = new FileHandle ">$request_filename" or die "can't create request file $request_filename";
$manifest_fh = new FileHandle ">$manifest_filename" or die "can't create request file $manifest_filename";

print $tracklet_fh $PS::MOPS::DX::header_DETECTION, "\n";
print $request_fh $PS::MOPS::DX::header_IODREQUEST, "\n";

# Input file handle.
my $sum_fh = new FileHandle($in_filename);

# Loop through lines in the summary file until we've read
# an entire track.  Then fetch each of the detections for
# the track and write out a detracklet using the link ID
# as the det ID.  We'll pass this on for orbit determination.
# Write out the detracklet as MIF-D, DX-D or MOBS.

my ($track_id, $tracklet_id, $line_no);
my $line;
my @stuff;              # accumulated stuff for this track.
my %trk_tracklet_ids;   # tracklet IDs making up current track
my $last_track_id = '';

my $num_tracks_processed = 0;

if ($tracklets) {
    while (defined($tracklet_id = <$sum_fh>)) {
        chomp $tracklet_id;
        $trk_tracklet_ids{$tracklet_id} = 1;
    }
    if (keys %trk_tracklet_ids > 0) {
        _out_track($oid, keys %trk_tracklet_ids);
        $num_tracks_processed = 1;
    }
}
else {
    while (defined($line = <$sum_fh>)) {
        chomp $line;
        ($track_id, $tracklet_id, $line_no) = split /\s+/, $line, 3;

        if ($last_track_id and ($track_id ne $last_track_id)) {
            _out_track($last_track_id, keys %trk_tracklet_ids);
            $num_tracks_processed += 1;
            %trk_tracklet_ids = ();
        }
        else {
            $trk_tracklet_ids{$tracklet_id} = 1;
        }
        $last_track_id = $track_id;
    }
    if (keys %trk_tracklet_ids > 0) {
        _out_track($last_track_id, keys %trk_tracklet_ids);
        $num_tracks_processed += 1;
    }
}

# Clean up.
print STDERR "Processed $num_tracks_processed tracks.\n";
close $tracklet_fh;
print STDERR "Wrote $out_fileroot.in.tracklet.\n";
close $request_fh;
print STDERR "Wrote $out_fileroot.in.request.\n";

# Print manifest.
print $manifest_fh <<"EOF";
$out_fileroot.in.tracklet
$out_fileroot.in.request
$out_fileroot.in.manifest
EOF
print STDERR "Wrote $out_fileroot.in.manifest.\n";
exit;


sub _count_nights {
    my ($daref) = @_;           # detections array ref
    return 0; 
}


sub _out_track {
    my ($track_id, @tracklet_ids) = @_;
    my @tracklets = map { modct_retrieve($inst, trackletId => $_) } @tracklet_ids;  # fetch all tracklets
    my @detections; # flattened detection list
    my $nobs;       # number observations
    my $ntrk;       # number of tracklets
    my $nn;         # number of nights observed

    @tracklets = sort { $a->extEpoch <=> $b->extEpoch } @tracklets;     # sort by tracklet central time
    foreach my $trk (@tracklets) {
        # If we have already printed the tracklet, skip.
        if (!$printed_tracklet_ids{$trk->trackletId}) {
            foreach my $d (@{$trk->detections}) {
                $d->detId($trk->trackletId);       # hijack detId for tracklet listing

                #XXX hello
    #            $d->objectName('N' . $trk->trackletId) unless $d->objectName;       # just for andrea's code
                $d->objectName('NS') unless $d->objectName;                          # nonsynthetic => 'NS'
                print $tracklet_fh modx_toDETECTION($d), "\n";
            }
        }
        $printed_tracklet_ids{$trk->trackletId} = 1;   # record that we've printed it

        push @detections, @{$trk->detections};  # all all dets to list for this track
    }

    $nobs = scalar @detections;
    $ntrk = scalar @tracklets;
    $nn = _count_nights(\@detections);

    print $request_fh modx_toIODREQUEST(
        ID_OID => $track_id,
        TRACKLETS_REF => \@tracklets,
        N_OBS => $nobs,
        N_NIGHTS => $nn,
    ), "\n";
}


=head1 NAME

dxlt2iodreq - Create DX-formatted IOD requests from LinkTracklets output

=head1 SYNOPSIS

dxlt2iodreq [options] INFILE
dxlt2iodreq [options] < INFILE

  --twopass : prepare files for two-pass Milani IOD
  --tracklets : input data is trackletIds, not .sum file
  --oid : force OID for attr/precov processing
  --help : show manpage

=head1 DESCRIPTION

Converts LinkTracklets .sum output into a set of files constituting
a Data Exchange (DX) IOD request.  

The .sum extension is stripped from the input filename and replaced with
.in.tracklet and .in.request for the two files.

The first file (.in.tracklet) is a list of tracklets in detection format, as follows:

!!OID TIME OBS_TYPE RA DEC APPMAG FILTER OBSERVATORY RMS_RA RMS_DEC RMS_MAG S2N SECRET_NAME
a 54066.6030936111 O 174.88887519003 1.45091592950294 23.016797548749 r 568 0.04168 0.04168 0.10302 11.04742 S122q6s
a 54066.6114336111 O 174.890832568147 1.4495993645786 22.810393688678 r 568 0.04168 0.04168 0.10301 11.04797 S122q6s
b 54061.6017136111 O 173.664408202258 2.23836225233214 23.1198867686599 r 568 0.04255 0.04255 0.10600 10.75118 S122q6s
b 54061.6100836111 O 173.666472562706 2.23704249486204 23.0437605202537 r 568 0.04255 0.04255 0.10599 10.75165 S122q6s
c 54057.6008236111 O 172.639543927614 2.87894462465172 23.1833154246991 r 568 0.04318 0.04318 0.10813 10.54892 S122q6s
c 54057.6091736111 O 172.641700318828 2.87758199186666 23.2038565431715 r 568 0.04318 0.04318 0.10813 10.54933 S122q6s
c 54057.6148136111 O 172.64313862655 2.8766739189789 22.9948494455133 r 568 0.04318 0.04318 0.10813 10.54960 S122q6s
c 54057.6231536111 O 172.645294113482 2.87533768227445 23.1383503022159 r 568 0.04318 0.04318 0.10812 10.55001 S122q6s
[etc]

The second file is a request ident header file, providing the tracklet-track associations.

!!ID_OID  NID  TRACKLET_OIDs  OP_CODE  N_OBS  N_SOLUTIONS   N_NIGHTS  ARC_TYPE
abc 3 a b c REQUEST_PRELIM 8 1 3 ?

The third file is a manifest, simply listing three filenames (.in.tracklet, .in.ident_header, .in.manifest).

=cut
