#!/usr/bin/perl

# 1) Determine mean, median and mode seeing for each field
#    - Use mode in ranking and in header
# 2) Apply coarse shifts from WCS information
# 3) Perform fine centering with imalign
# 4) Match seeing for groups of exposures with psfmatch
# 5) Stack similar exposures

# History
#  08June17  MM  created
#  08 Aug14  MM  overhaul

#
# Detailed help message
#

use Getopt::Long;
use Text::Wrap;

@usage = ("v1.0  MM  02May2008\n\nUsage: imacscombine (OBJECTS) \n\n");
@help = (
         "\t---------------\n ",
         "\tREQUIRED INPUTS\n ",
         "\t---------------\n ",
         "\tOBJECTS is a file containing all of the dithered\n",
	 "\texposures following and separated by commas.\n",
	 "\tFor example:\n\n",
         "\timg1,img2,img3,img4,img5\n",
	 "\timg6,img7,img8,img9,img10\n\n",
	 "\twould stack img1-5 and img2-10\n\n",
         "\t------\n ",
         "\tOUTPUT\n ",
         "\t------\n ",
         "\tThe output is an IRAF CL script named 'combine.cl'; run\n",
         "\tthis file at the cl> prompt with the following syntax:\n",
         "\tcl> cl < combine.cl\n",
#	 "\t--------\n",
#	 "\tSWITCHES\n",
#	 "\t--------\n",
#	 "\t--fast         : Perform linear interpolation rather than\n",
#	 "\t                 sinc17 interpolation when undithering.\n",

         );


if ($#ARGV lt 1) {
    if ($#ARGV eq 0) {
        if ($ARGV[0] eq "--help") {
            print @usage;
	    print @help;
            print "\n\n";
            exit;
        }
    } else {
        die "@usage\For detailed help: imacscombine --help\n\n";
    }
}

$speed = '';
GetOptions ('fast' => \$speed);
if ($speed ne '') {
    $speed='linear';
} else {
    $speed='sinc17';
}

open OBJ, $ARGV[0];
@obj = <OBJ>;
close OBJ;

@combine=();

foreach (@obj){  # Each SET of exposures #

    print "\n";

    ## First we will do psf-match and then stack with sigma rejection ##

    @line=parsecom($_);
    for $k (0..$#line){   # Each exposure #
	open PSFILE, "$line[$k].psf";
	@psfile = <PSFILE>;
	close PSFILE;
	
	## First populate @psf array ##
	$i=0;
	@psf=();
	foreach (@psfile){            # Each star in each exposure #
	    $i+=1;
	    if (($i>4)&&($i<$#psfile)){
		@psfline=parse($_);
		push @psf, "$psfline[3]\n";
	    }
	}

	## Now sort @psf array and compute mode##
	@psf = sort{$a <=> $b} (@psf);
	$fwhmmode = mode(@psf);
	
	## Now, drop upper 50% of list (extended sources) ##
	@clpsf=();
	for $i (0..int($#psf/2)){
	    push @clpsf, "$psf[$i]\n";
	}

	## Now, go through remaining and calculate mean and stdev ##
	$fwhm=0;
	$fwhmerr=0;
	for $i (0..$#clpsf){
	    $fwhm+=$clpsf[$i];
	}
	$fwhm=$fwhm/$#clpsf;
	for $i (0..$#clpsf){
	    $fwhmerr+=(($clpsf[$i]-$fwhm)**2);
	}
	$fwhmerr=$fwhmerr/$#clpsf;
	$fwhmerr=sqrt($fwhmerr);
	$fwhmmed=0;
	$fwhmmed+=$clpsf[int($#clpsf/2)];
	@stats=();
	push @stats, "$fwhm";
	push @stats, "$fwhmmode";
	push @stats, "$fwhmmed";
	@stats = sort{$a <=> $b} (@stats);
	
	print "$line[$k]: \n";
	printf("   FWHM_avg=%5.2f, FWHM_mode=%5.2f, FWHM_med=%5.2f, FWHM=%5.2f, FWHM_err=%5.2f\n", $stats[0], $stats[1], $stats[2], $stats[1], $fwhmerr);

	## Add to the header here ##
	push @combine, "hedit $line[$k] FWHM $stats[1] add+ verify-\n";
	push @combine, "hedit $line[$k] FWHMSIG $fwhmerr add+ verify-\n";

	## Determine if this will be the reference image ##
	if ($k==0){
	    $ref=$line[0];
	    $reffw=$stats[1];
	} else {
	    if ($stats[1]>$reffw) {
		$ref=$line[$k];
		$reffw=$stats[1];
	    }
	}

    }

    ## NOW we should be able to do psf matching! ##

    printf( "\nUsing %s(fw=%5.2f) as reference frame.\n", $ref,$reffw);
    print "***NOTE***: If this is anomolously high seeing, rerun\n";
    print "            imacscombine with this frame excluded!!!\n\n";


    ## First, make the reference list ##
    open POSFILE, "$ref.psf";
    @pos = <POSFILE>;
    close POSFILE;
    
    @refpos=();
    $i=0;
    ## Find median brightness among stars with good seeing ##
    @mag=();
    foreach (@pos){
	$i+=1;
	if (($i>4)&&($i<$#pos)){
	    @psfline=parse($_);
	    if (($psfline[3]>($reffw-0.3))&&($psfline[3]<$reffw+0.3)) {
		push @mag, "$psfline[2]";
	    }
	}
    }
    @mag = sort{$a <=> $b} (@mag);
    $medmag=$mag[int($#mag/2)];
    $maglo=$mag[int(3*$#mag/4)];
    $maghi=$mag[int(1*$#mag/4)];
    $i=0;
    printf(" Only using stars in brightness range from\n");
    printf(" %5.2f-%5.2f mag for psf-matching.\n\n", $maglo, $maghi);
    
    foreach (@pos){
	$i+=1;
	if (($i>4)&&($i<$#pos)){
	    @psfline=parse($_);
	    
	    ## Use point sources for PSF matching ##
	    if (($psfline[3]>($reffw-0.3))&&($psfline[3]<$reffw+0.3)) {
		push @refpos, "$psfline[0] $psfline[1]\n";
	    }
	}
    }

    unlink <$ref.ref>;       #remove old reference file
    open OUT, ">$ref.ref";
    print OUT @refpos;
    close OUT;

    if ($#line==0) {
	print "Only a single frame. Skipping the rest of the steps.\n";
	print "Copying aa* file to staa* file with no changes.\n";
	push @combine, "imcopy $line[$k] st$line[$k]\n";
	push @combine, "hedit st$line[$k] IMCMB001 $line[$k] add+ verify-\n";

    }
    else
    {



    # Now, prepare the frames to be stacked by making various 
    # lists and performing shifts. Be sure to perform shifts #
    # on bad pixel masks as well!

    @line=parsecom($_);
    $first=1;
    @input1=();
    @input2=();
    @output=();
    @kernel=();
    for $k (0..$#line){
	if ($line[$k] =~ $ref){                    
	} else {
	    if ($first==1){
		$first=0;
		push @input1, "sh$line[$k]";
		push @input2, "$line[$k]";
		push @output, "csh$line[$k]";
		push @kernel, "kernel$k";

	    } else {
		push @input1, ",sh$line[$k]";
		push @input2, ",$line[$k]";
		push @output, ",csh$line[$k]";
		push @kernel, ",kernel$k";
	    }

	    # First, align with WCS info #
	    push @combine, "wregister $line[$k] $ref sh$line[$k] interpolant=\"spline3\" verbose+ interactive- wcsinherit+ fitgeometry=\"general\" fluxconserve+\n";
	    # Now do fine sub-pixel centering with imalign #
	    push @combine, "imalign sh$line[$k] $ref $ref.ref sh$line[$k] interp_type=\"spline3\" verbose+ bigbox=7\n";

	    # Align masks with wregister, pixel precision is fine. #
	    push @combine, "wregister $line[$k]_bpm.pl $ref sh$line[$k]_bpm.pl interpolant=\"nearest\" verbose+ interactive- fitgeometry=\"general\" wcsinherit+ fluxconserve-\n";
	    push @combine, "hedit sh$line[$k] BPM \'sh$line[$k]_bpm.pl\' add+ verify-\n";
	    
	}
    }

    ## Don't do this anymore!! ##
    ## ----------------------- ##

    # Now, run psfmatching w/ Gaussian #
#    push @combine, "psfmatch ";
#    push @combine, @input1;
#    push @combine, " $ref $ref.ref ";
#    push @combine, @kernel;
#    push @combine, " output=\"";
#    push @combine, @output;
#    push @combine, "\" convolution=\"image\" verbose+ center+ background=median dnx=25 dny=25 pnx=15 pny=15 filter=\"model\" threshold=0.2\n";

#    push @combine, "del kernel?.fits\n";
#    push @combine, "del kernel??.fits\n";

    # Finally, stack images #
    push @combine, "imcombine $ref,";
#    push @combine, @output;
    push @combine, @input1;
    push @combine, " st$line[0] combine=\"average\" reject=\"ccdclip\" hsigma=4.0 lsigma=4.0 masktype=\"badvalue\" maskvalue=1 rdnoise=\"3.30\" gain=\"0.83\" snoise=\"0.0\" mclip+\n";

}
}

unlink <combine.cl>; #remove old scripts first
open OUT, ">combine.cl";
print OUT @combine;
close OUT;

print fill("\t","",("Now enter IRAF and run 'combine.cl':\n cl> cl <
combine.cl\n "));
print "\n";


#######################

sub mode {
    my($mode);
    
    ## First, find the most populated bin from 1.0-10.0 in 0.5 steps ##
    $bestbin=0.0;
    $bestnum=0;
    $bin=1.0;
    for $l (0..18){
	$bin+=0.5;
	$num=0;
	for $m (0..$#psf){
	    if (($psf[$m]>($bin-0.25))&&($psf[$m]<($bin+0.25))) { $num+=1 }
	}
	if ($num>$bestnum){
	    $bestnum=$num;
	    $bestbin=($bin);
	}
    }
    
    ## Now we know coarse mode... ##
    ## Take the preferred bin and +/- 0.5 to get average ##
    
    $mode=0.0;
    $num=0;
    for $m (0..$#psf){
	if (($psf[$m]>($bestbin-0.5))&&($psf[$m]<($bestbin+0.5))) {
	    $mode+=$psf[$m];
	    $num+=1;
	}
    }
    $mode=$mode/$num;
    
    return($mode);
}

#######################

sub parse {
    my(@parsed);
    @parsed = split(/\s+/,$_[0]);
    chomp(@parsed);
    #remove whitespace at beginning of line, if any
    while ($parsed[0] eq "") {shift(@parsed)}
    return(@parsed);
}

##########################
# @array = PARSECOM($line)
# Parse by commas
# 08feb11  DSNR  created
##########################

sub parsecom {
    my(@parsed);
    @parsed = split(/,+/,$_[0]);
    chomp(@parsed);
    #remove whitespace at beginning of line, if any
    while ($parsed[0] eq "") {shift(@parsed)}
    return(@parsed);
}
