# Create a "master" flatfield frame from a number of (dark-subtracted)
#   target image, by finding the median of them all.
#
# MWR 5/20/2000
#
# Ugly situation: must use a number of target frames to create median
#   flatfield; but must subtract dark from the target frames before
#   doing so.  For now, do this: make copies of the target frames,
#   subtract dark from the copies, combine these copies to make median
#   flatfield, then DELETE the copies.  We will subtract dark from
#   the originals of the target frames later.
# MWR 5/29/2000
# 
# Modify so that all temporary files are created in the output directory,
#   rather than the current working directory.
# MWR 1/20/2001
#
# Added "xvista_dir" to all calls to XVista programs
#   --  MWR 2/9/2001
#
# Convert the exposure time into a binned time before choosing the
#   master dark name with it.
#   --  MWR 4/6/2001
#
# Added a step: after having created the master flat in each filter,
#   we make a trimmed copy and look for bad regions in it.  We place
#   the list of bad regions into a mask file.
#   --  MWR 5/28/2001
#
# Big changes:
#      a. new routine "trim_input_list" to truncate list of input files if
#         it is larger than parameter "flat_maximages"
#      b. add param "twisky_flat", which controls how we choose input
#         files for the flatfield.
#               twisky_flat    1           use images of type "flat" only
#               twisky_flat    0           use images of type "object" only
#
#
#   -- MWR 8/3/2001



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

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


############################################################################
# create a median flatfield image from a set of image files.
#
# usage:  make_flat        param_file  
#
#            param_file          name of a file containing parameters
#                                    used to control production of
#                                    the flatfield frames
#
#
# Some of the major parameters are
#                                    
#            input_dir           directory in which input files reside
#            
#            output_dir          directory into which output flats are placed
#                                    (and temporary files)
#
#            output_file         stem for name of output median image
#                                    (will have filter added to name)
#            
# DO scale the files before combining them (so we can combine frames
#   with different sky levels properly)
#    
# Returns
#   0          if all goes well
#   1          if there's a problem
#
proc make_flat { param_file } {
  global debug

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

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

  # get information from the param file
  if { [file exists $param_file] != 1 } {
    puts stderr "make_flat: 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"]

  set box_number [get_param $param_file "box_number"]
  set box_row_center [get_param $param_file "box_row_center"]
  set box_col_center [get_param $param_file "box_col_center"]
  set box_row_size   [get_param $param_file "box_row_size"]
  set box_col_size   [get_param $param_file "box_col_size"]

  set mask_gridsize  [get_param $param_file "mask_gridsize"]
  set mask_minsig    [get_param $param_file "mask_minsig"]
  set mask_minpix    [get_param $param_file "mask_minpix"]

  set twisky_flats   [get_param $param_file "twisky_flats"]
  set flat_maximages [get_param $param_file "flat_maximages"]


  # get information we'll need to call "sub_dark" on the target
  #   frames before we combine them to create the master flat
  if { [get_sub_dark_info box_start dark_cols \
                          trim_start_row trim_end_row] != 0 } {
    puts stderr "make_flat: get_sub_dark_info returns with error"
    return 1
  }


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

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

    if { $debug > 1 } {
      puts "  make_flat: filter $filter"
    }

    if { $twisky_flats == 0 } {

      # We are to use a bunch of night-sky images of target objects
		#   to create master flatfield frames.

  
      # I want to pick a "best" subset of frames in this filter
      #   to use in creating a master flat.  Find the maximum
      #   exposure time among all frames in this filter, and then
      #   select only those which which have that exposure time
      set max_exptime [get_max_exptime $filter]
      puts "max_exptime for filter $filter is $max_exptime"

      # create a list of files with this filter and type "object",
      #   AND with the maximum exposure time

      set input_list [select_binned_exposures $filter $max_exptime "object"]
      if { [llength $input_list] < 1 } {
        continue
      }
      if { $debug > 1 } {
        puts "  make_flat: input images are $input_list"
      }

    } else {

	   # we are to use special twilight-sky images, of type "flat", 
		#   to create the master flatfield frame.
      set param_list [list [list "filter" $filter] \
                           [list "type" "flat"] \
                           [list "include" "1"] ]
      set input_list [select_input_list $param_list]
      if { [llength $input_list] < 1 } {
        puts stderr "make_flat: no images for filter $filter?"
        return -1
      }
      if { $debug > 1 } {
        puts "  make_flat: input images are $input_list"
      }

    }
  

      # pick only the first "flat_maximages" images in the list
		set input_list [trim_input_list $input_list $flat_maximages ]

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


      # make a copy of each input file in the current directory
      #   subtract dark from these copies
      set mod_input_list {}
      foreach target_file $input_list {

        # figure out the appropriate dark frame to subtract 
        if { [single_image_info $target_file klist] != 0 } {
          puts stderr "make_flat: single_image_info fails for $target_file"
          return 1
        }
        set exposure [get_image_value $klist exposure]
        set exposure [get_binned_exptime $exposure]
        set master_dark [master_dark_name $filter $exposure]

        set mod_target_file QQ$target_file
        exec /bin/rm -f $output_dir/$mod_target_file 
        exec cp $input_dir/$target_file $output_dir/$mod_target_file
        exec chmod 644 $output_dir/$mod_target_file
        if { [sub_dark $output_dir $mod_target_file $master_dark \
                                      $box_start $dark_cols \
                                      $trim_start_row $trim_end_row] != 0 } {
          puts stderr "make_flat: sub_dark fails for file $mod_target_file"
          return 1
        }
        lappend mod_input_list $mod_target_file
      }


      # create the median flatfield frame.  
      #    first, we define a box to use for scaling the images
      #         to a common mean -- we'll pick the central
      #         quarter of the image
      #    next, we call the "median" program to do the work

      # define the box
      exec $xvista_dir/box $box_number cr=$box_row_center cc=$box_col_center \
                           nr=$box_row_size   nc=$box_col_size

      set scale_arg $box_number
      if { [make_median $output_dir $mod_input_list \
                                 $output_file_name $scale_arg] != 0 } {
        puts stderr "make_flat: make_median returns with error"
        return 1
      }

      # put comment into the output median dark image for tracking purposes
      set datestr [exec date]
      exec $xvista_dir/comment $output_file_name \
                   "created by make_flat.tcl on $datestr"

      # delete the modified versions of the target files
      foreach mod_input_file $mod_input_list {
        if { 1 } {
          exec rm -f $output_dir/$mod_input_file
        } else {
          puts "       should call rm $output_dir/$mod_input_file now"
        }
      }


      # Create a "mask" file which lists the bad regions found in 
      #   the master flatfield.  We will use this mask file later on
      #   to mark certain stellar measurements as suspect.
      #   The "mask" file has the same name as the master flatfield,
      #   but an extension of ".msk".  
      #
      # Step 1: create a temporary copy of the master flatfield
      set master_flat_copy $output_file_name.QQ
      exec /bin/rm -f $master_flat_copy 
      exec cp $output_file_name $master_flat_copy
      exec chmod 644 $master_flat_copy
      
      # Step 2: trim the copy of the flatfield to remove edge rows and cols
      if { [trim_image $master_flat_copy] != 0 } {
        puts stderr "make_flat: trim_image fails for $master_flat_copy"
	return 1
      }

      # Step 3: create the mask file for this trimmed flatfield
      set mask_file [master_mask_name $filter]
      if { [create_mask $master_flat_copy $mask_file \
                        $mask_gridsize $mask_minsig $mask_minpix ] != 0 } {
        puts stderr "make_flat: create_mask returns with error"
        return 1
      }

      # Step 4: remove the temporary, trimmed copy of the master flat
      exec /bin/rm -f $master_flat_copy

  }

  return 0
}


############################################################################
# PROCEDURE: create_mask
#
# DESCRIPTION: prepare to call the XVista program "mask" on a trimmed copy
#              of a master flatfield frame.  Then call "mask", and place
#              the output into the given file in the output_dir.
#
# RETURNS:
#    0         if all goes well
#    1         if something goes wrong
#
proc create_mask { trimmed_flat output_mask_file \
                   mask_gridsize mask_minsig mask_minpix } {
  global debug

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

  # sanity check
  if { [file exists $trimmed_flat] != 1 } {
    puts stderr "create_mask: file $trimmed_flat doesn't exist"
    return 1
  }

  # run the XVista mask command with the given args
  set xvista_dir [get_directory "xvista_dir"]
  if { $xvista_dir == {} } {
    puts stderr "create_mask: get_directory fails for xvista_dir"
    return 1
  }
  exec $xvista_dir/mask $trimmed_flat gridsize=$mask_gridsize \
                 minsig=$mask_minsig minpix=$mask_minpix \
		 outfile=$output_mask_file
  
  return 0
}



############################################################################
# PROCEDURE: get_max_exptime
#
# DESCRIPTION: find the longest (binned) exposure time among all the images
#              taken in the given filter.  Return that exposure time.
#
# RETURNS:  
#     the max exposure time              if all goes well
#     -1                                 if something goes wrong
#
proc get_max_exptime { filter } {
  global debug

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

  set param_list [list [list "filter" $filter] \
                       [list "type" "object"] ]
  set input_list [select_input_list $param_list]
  if { [llength $input_list] < 1 } {
    puts stderr "get_max_exptime: no images for filter $filter?"
    return -1
  }

  # figure out the maximum exposure time among all these files
  set max 0
  foreach file $input_list {
    if { [single_image_info $file klist] != 0 } {
      puts stderr "get_max_exptime: single_image_info fails"
      return -1
    }
    set this_exptime [get_image_value $klist exposure]
    set this_exptime [get_binned_exptime $this_exptime]
    if { $this_exptime > $max } {
      set max $this_exptime
    }
  }
  if { $debug > 1 } {
    puts "get_max_exptime: max exposure time is $max"
  }

  return $max
}


############################################################################
# PROCEDURE: trim_input_list
#
# DESCRIPTION: Given a (possibly huge) list of images from which we'll
#              create a flatfield, pick the first "maximages" of them;
#              discard the remainder.
#
# RETURNS:
#      a new copy of the input list, with at most "maximages" members
#
proc trim_input_list { input_list maximages } {
  global debug

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

  if { $debug > 1 } {
	 set len [llength $input_list]
    puts "trim_input_list: input  list has $len members: "
  }

  if { [llength $input_list] <= $maximages } {
    return $input_list
  }

  set index 0
  set newlist {}
  while { $index < $maximages } {
    lappend newlist [lindex $input_list $index]
    incr index
  }
  if { $debug > 1 } {
	 set len [llength $newlist]
    puts "trim_input_list: output list has $len members: ..$newlist.. "
  }

  return $newlist
}
    

  



#######################
# Here, we start the namespace "Make_Flat", which we use to 
#   store information about the master flat file name(s)
#   
# 
namespace eval Make_Flat {
  namespace export init_master_flat master_flat_name master_mask_name
  variable Master_Flat_Stem {}


#############################################################################
# Initialize the "static" variable "Master_Flat_Stem" by reading
#   its value from the given param file
# 
#
proc init_master_flat { param_file } {
  global debug
  variable Master_Flat_Stem
  
  if { $debug > 0 } {
    puts "entering init_master_flat"
  } 
  
  if { [file exists $param_file] == 0 } {
    puts stderr "init_master_flat: can't find param file $param_file"
    return 1
  } 
  set output_file      [get_param $param_file "output_file"]
  set output_dir       [get_param $param_file "output_dir"]
  set Master_Flat_Stem $output_dir/$output_file
  
  return 0
} 




#############################################################################
# Given a filter name, create a name for a master flatfield image.
#   
# usage:   master_flat_name      param_file filter 
# 
#              filter            name of filter (really identifies camera)
#              
# Returns
#   the name of the master flat
#   
proc master_flat_name { filter } {
  global debug
  variable Master_Flat_Stem

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

  set file_stem $Master_Flat_Stem
  
  set output_file_name \
            [format "%s_%s.fts" $file_stem $filter]

  return $output_file_name
}


#############################################################################
# Given a filter name, create a name for a master flatfield's mask file.
#   (The mask file holds a list of the bad regions in the image)
#   
# usage:   master_mask_name      filter 
# 
#              filter            name of filter (really identifies camera)
#              
# Returns
#   the name of the master mask file
#   
proc master_mask_name { filter } {
  global debug
  variable Master_Flat_Stem

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

  set file_stem $Master_Flat_Stem
  
  set output_file_name \
            [format "%s_%s.msk" $file_stem $filter]

  return $output_file_name
}


}
# end of the namespace Make_Flat

