# Create a "master" dark frame from a number of dark images,
#   by finding the median of them all.
#
# MWR 2/27/2000
#
# MWR 5/20/2000
# 
# Added a namespace "Make_Dark" within this file, which 
#   allows us to have "static" variables.  We use them to
#   store the stem of the "master_dark" file name.
#   Must call an initialization routine
#        init_master_dark
#   at start of program execution
#   
# MWR 5/23/2000
#
# Added "xvista_dir" to all calls to XVista programs
#   --  MWR 2/9/2001
#
# Modified "select_binned_exposures" so that it only includes dark frames
#   with the "include" flag set to 1.
#   --  MWR 2/8/2003

if { [catch { set debug } ] == 1 } {
  set debug 2
}

source param.tcl
source proc_file_list.tcl
source make_median.tcl


############################################################################
# create a median dark image from a set of image files.
#
# usage:  make_dark    param_file
# 
# where
#            param_file          is an ASCII file with parameters to 
#                                       control the production of master dark
#                                       frames.
#
# Some of the major parameters in it are:
#
#            input_dir           directory in which input files reside
#            
#
#            output_dir          directory into which to place output files
#
#            output_file         stem for name of output median image
#                                       (filter and exposure time will 
#                                        be appended)
#            
# Do NOT scale the files before combining them (this means we should 
#           combine only images with identical exposure times)
#    
# Returns
#   0          if all goes well
#   1          if there's a problem
#
proc make_dark { param_file } {
  global debug

  if { $debug > 0 } {
    puts "entering make_dark "
  }

  # the XVista programs live in this directory
  set xvista_dir [get_directory "xvista_dir"]
  if { $xvista_dir == {} } {
    puts stderr "make_dark: get_directory fails for xvista_dir"
    return 1
  }

  # get information from the param file
  if { [file exists $param_file] != 1 } {
    puts "make_dark: can't open param file $param_file"
    return 1
  }

  set input_dir [get_param $param_file "input_dir"]
  set output_dir [get_param $param_file "output_dir"]
  set output_file [get_param $param_file "output_file"]
  



  # first, we identify all the filters in the input list of files
  if { [get_list_info filter_list "filter"] != 0 } {
    puts stderr "make_dark: get_list_info fails"
    return 1
  }

  # next, we identify all the exposure times in the input list of files
  if { [get_list_info exposure_list "exposure"] != 0 } {
    puts stderr "make_dark: get_exposure_list fails"
    return 1
  }

  # turn the list of actual exposure times into a list of binned
  #   exposure times, in which we discard duplicate values.
  set binned_exposure_list [bin_uniq_exposures $exposure_list]

  # okay, we're ready to create dark frames.  
  #   We loop over each filter, and over each (binned) exposure time
  #   If there are no input dark images for a given combination,
  #     we print a warning message, skip it and move on 
  foreach filter $filter_list {

    if { $debug > 1 } {
      puts "  make_dark: filter $filter"
    }
  
    foreach exposure $binned_exposure_list {

      if { $debug > 1 } {
        puts "  make_dark: exposure $exposure"
      }

      # create a list of files with this combination of filter
      #   and (binned) exposure
      set input_list [select_binned_exposures $filter $exposure "dark"]
      if { [llength $input_list] < 1 } {
        continue
      }

      # create an appropriate output file name
      set output_file_name [master_dark_name $filter $exposure]

      # create the median dark frame
      set scale_arg 0
      if { [make_median $input_dir $input_list \
                                 $output_file_name $scale_arg] != 0 } {
        puts stderr "make_dark: make_median returns with error"
        return 1
      }

      # put comments into the output median dark image for tracking purposes
      set str [format "this is a master dark for exposures of %.0f seconds" \
                          $exposure]
      exec $xvista_dir/comment $output_file_name "$str"
      set datestr [exec date]
      exec $xvista_dir/comment $output_file_name "created by make_dark.tcl on $datestr"

    }

  }

  return 0
}


############################################################################
# PROCEDURE: get_binned_exptime
#
# usage:  get_binned_exptime  exptime
# 
# where
#            exptime             is the exposure time of an image (seconds)
#
#          This routine returns a "binned" equivalent exposure time.
#          We need the "binned" versions because the exposure times 
#          recorded in FITS headers have a small random scatter, yet
#          we want to process them identically.
#
#          If the given exposure time can't be parsed properly,
#          print error message and halt program execution.
#    
# Returns
#     binned exposure time
#
proc get_binned_exptime { exptime } {
  global debug

  if { $debug > 0 } {
    puts "entering get_binned_exptime "
  }

  # This the single value you'll probably want to modify ...
  set bin_size  10

  set start_bin [expr 0 - ($bin_size/2)]
  set max_bin 10000
  set bin -1
  while { $start_bin < $max_bin } {
    set end_bin [expr $start_bin + $bin_size]
    if { $exptime < $end_bin } {
      set bin [expr ($start_bin+$end_bin)/2]
      break
    }
    set start_bin $end_bin
  }
  if { $bin == -1 } {
    error "warning: get_binned_exptime fails for exptime $exptime"
  }

  return $bin
}


############################################################################
# PROCEDURE: bin_uniq_exposures
#
# usage:  bin_uniq_exposures    exposure_list
# 
# where
#            exposure_list       is a list of actual exposure times
#
#         This routine replaces each actual exposure time in the given
#         list with its binned equivalent.  It then discards all
#         duplicate entries.  The result is a list of all the different
#         binned exposure times.
#    
# Returns
#     the binned exposure list
#
proc bin_uniq_exposures { exposure_list } {
  global debug

  if { $debug > 0 } {
    puts "entering bin_uniq_exposures "
  }

  set binned_list {}
  foreach exptime $exposure_list {
    set binned_time [get_binned_exptime $exptime]
    if { [lsearch $binned_list $binned_time] == -1 } {
      lappend binned_list $binned_time
    }
  }

  if { $debug > 1 } {
    puts "bin_uniq_exposures: first original, then binned"
    puts $exposure_list
    puts $binned_list
  }

  return $binned_list
}


############################################################################
# PROCEDURE: select_binned_exposures
#
# usage:  select_binned_exposures  filter exptime type
# 
# where
#            filter              is the filter of interest
#            exptime             is the (binned) exposure time of interest
#            type                is the type of interest
#
#          We look for all exposures which have the given filter, the
#          given type (probably "dark"), and which have a binned exposure
#          time equal to the given exposure time.
#
#          We also include only images which have the "include" flag set
#          to 1.
#
# Returns
#     a list of all matching exposures
#
proc select_binned_exposures { filter exptime type } {
  global debug

  if { $debug > 0 } {
    puts "entering select_binned_exposures "
  }

  set ret_list {}

  # we start off with all exposures which match the filter and type
  #   and which have "include" set to 1
  set param_list [list [list "filter" $filter] \
                       [list "type" $type] \
                       [list "include" "1"] ]
  set input_list [select_input_list $param_list]
  if { [llength $input_list] < 1 } {
    return $ret_list
  }

  # okay, now we have to check the exposure time of each candidate image
  foreach candidate $input_list {
    set this_image_info [single_image_info $candidate this_info_list]
    set this_exptime [get_image_value $this_info_list "exposure"]
    set this_binned_exptime [get_binned_exptime $this_exptime]
    if { $this_binned_exptime == $exptime } {
      lappend ret_list $candidate
    }
  }

  if { $debug > 3 } {
    puts "select_binned_exposures: ret_list is $ret_list"
  }
  return $ret_list

}




#######################
# Here, we start the namespace "Make_Dark", which we use to 
#   store information about the master dark file name(s)
#   
namespace eval Make_Dark {
  namespace export init_master_dark master_dark_name
  variable Master_Dark_Stem {}



#############################################################################
# Initialize the "static" variable "Master_Dark_Stem" by reading
#   its value from the given param file
# 
proc init_master_dark { param_file } {
  global debug
  variable Master_Dark_Stem

  if { $debug > 0 } {
    puts "entering init_master_dark"
  }

  if { [file exists $param_file] == 0 } {
    puts stderr "init_master_dark: can't find param file $param_file"
    return 1
  }

  set output_dir [get_param $param_file "output_dir"]
  set output_file [get_param $param_file "output_file"]
  set Master_Dark_Stem $output_dir/$output_file

  return 0
}


#############################################################################
# Given a filter name, and an exposure time, create a name for a
#   master dark image.
#   
# usage:   master_dark_name      filter exposure
# 
#              param_file        parameter file which contains the
#                                    stem for master dark name
#              filter            name of filter (really identifies camera)
#              exposure          exposure time, in seconds
#              
# Returns
#   the name of the master dark
#   
proc master_dark_name { filter exposure } {
  global debug
  variable Master_Dark_Stem

  if { $debug > 0 } {
    puts "entering master_dark_name "
  }

  set file_stem $Master_Dark_Stem
  set output_file_name \
            [format "%s_%s_%.0f.fts" $file_stem $filter $exposure]

  return $output_file_name
}



}
# end of namespace Make_Dark
