# These routines act on the "file list", an ASCII file with one line
#   per image taken by the Mark IV.  Each line looks something
#   like this:
#   
#  name filter JD exposure_time type ra dec sky skysig nstar zeropoint include
#
# MWR 5/20/2000
#
# Routines herein:
#   
#   init_list_name             sets a "static" variable within this
#                                   namespace to the name of a file
#                                   containing information about all
#                                   the images in this run.  Must be
#                                   called before any other proc in 
#                                   this file
#
#   get_list_name              returns the value of the "static" variable
#                                   which holds the list file's name
#
#   init_list_fields           sets a "static" variable within this 
#                                   namespace to the list of fields we
#                                   record for each image.  Must be called
#                                   once, at star of pipeline
#
#   count_list_fields          return the number of fields we expect
#                                   to see in each line of the list file
#
#   get_list_info              return a list of values for one parameter
#                                   such as all the types of filter, 
#                                   or all the different exposure times
#                                   
#   select_input_list          return a list of image names, for images
#                                   with parameter(s) which match 
#                                   given values:
#                                      "all V-band images
#                                      "all V-band and 'dark' images"
#
#   single_image_info          return a list of pairs: { keyword value }
#                                   with information about a single image,
#                                   given the image's name
#
#   get_image_value            given a list of pairs: { keyword value }
#                                   and a keyword, return the value
#                                   corresponding to the keyword
#
#   image_include_check        given an image name, check to see if its 
#                                   "include" value is 1 (meaning we should
#                                   process it).  
#
#                                   
# This file contains a namespace "Proc_File_List", 
#   to hold some private variables.  It allows us to call an initialization
#   routine which reads information from the param file,
#   then save those values so we can call other routines 
#   without having to pass the param file as a parameters again
#
#
# Removed "airmass" from set of values listed in file.
#     --- MWR 1/20/2001   
#
# Added the three elements "skyval", "nstar" and "zp" to the format
#   of each line in the list file.  Also added proc "replace_image_value"
#   to edit one element of a specific line.
#     --- MWR 2/7/2003
#
# Added the new element "include" to end of each line in list file. 
#   Also added new proc "image_include_check", which is simply a convenience;
#   it's pretty simple.
#     --- MWR 2/8/2003
#
# Added the new element "skysig" to each line in list file.  Also modified
#   the file to read a list of the field names from the "make_list.param"
#   file, and refer to that list when deciphering each line read from
#   the "make_list.out" file.  This means that we only need to change
#   code in ONE place if we add a new field to the list file.
#     --- MWR 2/9/2003
#
# Added pair of funcs "init_skip_bad" and "designate_bad".  The first
#   is called once, when pipeline starts; it sets a static variable.
#   The other is called when we discover that an image fails some test;
#   if the user desires, it changes the "include" field of that image's
#   record to zero, so that it is skipped in all subsequent processing.
#     --- MWR 2/9/2003
#
# Added "get_list_name" proc, so that we can make a copy of the 
#   completed file list at the very end of the pipeline.
#     --- MWR 4/11/2003


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


namespace eval Proc_File_List {
  namespace export init_list_name init_list_fields count_list_fields \
                   get_list_info get_list_name \
                   select_input_list single_image_info get_image_value \
                   replace_image_value image_include_check \
                   init_skip_bad designate_bad
  variable List_File_Name {}
  variable List_File_Fields {}
  variable Skip_Bad_Images {}


#############################################################################
# Set the "list_file" name.  The "list_file" is a file containing a list
#   of raw image file names, together with properties such as
#   filter, exposure time, etc.
#   
# This proc sets a local "static" variable, List_File_Name,
#   which is used by several other procs within this namespace
# 
proc init_list_name { list_file } {
  global debug
  variable List_File_Name

  if { $debug > 0 } {
    puts "entering init_list_name"
  }
  set List_File_Name $list_file

  return 0
}


#############################################################################
# Get the "list_file" name and return it.
# 
proc get_list_name { } {
  global debug
  variable List_File_Name

  if { $debug > 0 } {
    puts "entering get_list_name"
  }
  return $List_File_Name 
}



#############################################################################
# Initialize information about the fields in the "make_list" file.
#   The argument is the name of the "make_list" param file.
#  
# This proc sets a local "static" variable, List_File_Fields,
#   which is used by several other procs within this namespace.
#
proc init_list_fields { param_file } {
  global debug
  variable List_File_Fields

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

  set line [get_param $param_file "list_fields"]
  set List_File_Fields [lindex $line 0]
  if { $debug > 3 } {
    set nfield [llength $List_File_Fields]
    puts "init_list_fields: there are $nfield fields "
    set index 0
    foreach field $List_File_Fields {
      puts "init_list_fields: field $index is $field "
      incr index
    }
  }
  return 0
}


#############################################################################
# PROCEDURE: count_list_fields
#
# Return the number of fields we expect to see in each line of the
#   "make_list.out" file.  Just count the elements in the static
#   List_File_Fields variable.
#
proc count_list_fields { } {
  global debug
  variable List_File_Fields
 
  if { $debug > 0 } {
    puts "entering count_list_fields"
  }

  set num [llength $List_File_Fields]
  return $num
}


#############################################################################
# Set the "Skip_Bad_Images" variable.  The "get_skip_bad" proc will
#   read it later on.
# 
proc init_skip_bad { skip_bad_value } {
  global debug
  variable Skip_Bad_Images

  if { $debug > 0 } {
    puts "entering init_skip_bad"
  }
  set Skip_Bad_Images $skip_bad_value
  if { $debug > 0 } {
    if { $Skip_Bad_Images == 0 } {
      puts "  pipeline will process images which fail test(s)"
    } else {
      puts "  pipeline will skip    images which fail test(s)"
    }
  }

  return 0
}




#############################################################################
# Parse the "image list" file (created by make_list)
#   to find all the images of a particular sort.  
#   Note that "init_list_name" must have been called before this proc.
#   
# usage:  get_list_info    info_list info_type
#
#               info_list          is a variable we will set to the 
#                                       list of name, or filters, or etc.
#                                       encountered among the images
#                                       in list_file
#
#               info_type          is   "name"      to gather list of names
#                                       "filter"    to gather list of filters
#                                       "jd"        to gather list of Julian
#                                                       date values
#                                       "exposure"  to gather list of 
#                                                       exposure times
#                                       "type"      to gather list of types
#                                       "ra"        to gather list of RA vals
#                                       "dec"       to gather list of Dec vals
#                                       "sky"       to gether list of sky vals
#                                       "skysig"    to gether list of sky sigma
#                                                       values
#                                       "nstar"     to gether list of number 
#                                                       of stars in each image
#                                       "zeropoint" to gather list of 
#                                                       photometric zeropoints
#                                       "include"   to gather list of flags
#                                                       
#                                       
# Returns
#    0            if all goes well
#    1            if there's a problem
#
proc get_list_info { info_list info_type } {
  upvar $info_list i_list
  global debug
  variable List_File_Name 
  variable List_File_Fields


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

  set list_file $List_File_Name
  if { [catch { set fid [open $list_file "r"] } ] != 0 } {
    puts stderr "get_list_info: can't open file $list_file"
    return 1
  }


  # we walk through the list file.
  #   skip any lines which start with a pound sign
  #   skip (and print warning) for any lines which don't have the 
  #      expected format
  #      
  set i_list ""
  while { [gets $fid line] != -1 } {

    # skip comments
    if { [regexp \#.* $line] == 1 } {
      continue
    }

    # split line into pieces, expect exactly the right number of words
    if { [llength $line] != [llength $List_File_Fields] } {
      puts stderr "get_list_info: bad line in file $list_file, will skip"
      puts stderr $line
      continue
    }

    # okay, this looks like a good entry
    #   parse it and pick out the desired parameter value
    foreach field $List_File_Fields {
      set index [lsearch $List_File_Fields $field]
      if { [string compare $info_type $field] == 0 } {
        set this_item [lindex $line $index]
        if { $debug > 1 } {
          puts "    this $field is $this_item"
        }
      }
    }

    if { [lsearch $i_list $this_item] == -1 } {
      lappend i_list $this_item
      if { $debug > 1 } {
        puts "    adding to list, now it is $i_list"
      }
    }

  }

  # all done
  close $fid

  if { $debug > 1 } {
    puts "get_list_info: returning list $i_list "
  }


  return 0
}



#############################################################################
# Parse the "image list" file, created by make_list.
#   Given specific value(s) for each parameter(s) of interest,
#   pick out the images in the list which have the given specific
#   value(s)
#   Note that "init_list_name" must have been called before this proc.
#   
# usage:  select_input_list    param_pair_list 
#
#                                       
#               param_pair_list    is a list of pairs of things.  Each pair
#                                       consists of a parameter name 
#                                       (such as "filter" or "type")
#                                       and an associated value.  
#                                       Examples:
#                                       
#                                           { { filter V } }
#                                       or     
#                                           { { filter V } { exposure 60 } }
#                                       
#                                       
# Returns
#    a list of the appropriate image files
#    {}                   if given bad input
#
proc select_input_list { param_pair_list } {
  global debug
  variable List_File_Name
  variable List_File_Fields


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

  set list_file $List_File_Name
  if { [catch { set fid [open $list_file "r"] } ] != 0 } {
    puts stderr "select_input_list: can't open file $list_file"
    return {}
  }

  # we walk through the list file.
  #   skip any lines which start with a pound sign
  #   skip (and print warning) for any lines which don't have the 
  #      expected format
  #      
  set the_input_list ""
  while { [gets $fid line] != -1 } {

    # skip comments
    if { [regexp \#.* $line] == 1 } {
      continue
    }

    # split line into pieces, expect exactly correct number of words
    if { [llength $line] != [llength $List_File_Fields] } {
      puts stderr "select_input_list: bad line in file $list_file, will skip"
      puts stderr $line
      continue
    }

    if { $debug > 1 } {
      puts "   next is $line"
    }

    # by default, we will add this entry to the list (so set 'addit' to 1),
    #   but we are going to check each provided parameter, and if any
    #   don't match the provided value, we disqualify this entry
    #   (by setting 'addit' to 0)
    #    
    set addit 1
    foreach pair $param_pair_list {

      if { [llength $pair] != 2 } {
        puts stderr "select_input_list: bad param pair $pair"
        return {}
      }
      set this_param [lindex $pair 0]
      set this_value [lindex $pair 1]

      # now check to see if this line meets the condition(s)
      #   if it does NOT, we set "addit" to 0
      set index [lsearch $List_File_Fields $this_param]
      if { $index == -1 } {
        puts stderr "select_input_list: bad param keyword $this_param"
        return {}
      }
      set line_value [lindex $line $index]
      if { [string compare $this_value $line_value] != 0 } {
        set addit 0
        if { $debug > 1 } {
           puts "      disqual because $this_param $line_value not $this_value"
        }
      }

    }

    # if this still looks like a good entry, add it to the output list
    if { $addit == 1 } {
      lappend the_input_list [lindex $line 0]
      if { $debug > 1 } {
        puts "    adding to list, now it is $the_input_list"
      }
    }

  }

  # all done
  close $fid

  if { $debug > 1 } {
    puts "select_input_list: returning list $the_input_list "
  }

  return $the_input_list
}



#############################################################################
# Parse the "image list" file, created by make_list, 
#   Given one specific image name,
#   look for that name in the image list and (if found) return 
#   information about the image: name, filter, etc.
#   
# usage:  single_image_info    name  value_pair_list
#
#                                       
#               name               is the name of the image of interest
#
#               value_list         will be set on output to a
#                                  list of pairs of keywords and values:
#                                       { name "foobar.fts" } { filter "V" }
#                                  
#                                       
# Returns
#    0                    if all goes well
#    1                    if there's a problem
#
proc single_image_info { name value_list } {
  upvar $value_list v_list 
  global debug
  variable List_File_Name
  variable List_File_Fields

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

  set list_file $List_File_Name
  if { [catch { set fid [open $list_file "r"] } ] != 0 } {
    puts stderr "single_image_info: can't open file $list_file"
    return 1
  }

  # we walk through the list file
  #   skip any lines which start with a pound sign
  #   skip (and print warning) for any lines which don't have the 
  #      expected format
  #      
  set v_list ""
  while { [gets $fid line] != -1 } {

    # skip comments
    if { [regexp \#.* $line] == 1 } {
      continue
    }

    # split line into pieces, expect exactly correct number of words
    if { [llength $line] != [llength $List_File_Fields] } {
      puts stderr "single_image_info: bad line in file $list_file, will skip"
      puts stderr $line
      continue
    }

    if { $debug > 1 } {
      puts "   next is $line"
    }

    set name_index [lsearch $List_File_Fields "name"]
    if { $name_index == -1 } {
      puts stderr "single_image_info: can't find 'name' field?!"
      return 1
    }
    set this_name [lindex $line $name_index]
    if { [string compare $this_name $name] == 0 } {
      # found it!  Build the list
      foreach field $List_File_Fields {
        set index [lsearch $List_File_Fields $field]
        lappend v_list [list $field [lindex $line $index]]
      }

      # no need to check other images 
      break
    }

  }


  # all done
  close $fid

  if { $debug > 1 } {
    puts "single_image_info: returning list $v_list "
  }

  return 0
}


#############################################################################
# Given a list of paired items describing an image, look for a particular
#   keyword and return the corresponding value.
#   
# usage:    get_image_value   image_info_list  keyword
# 
# where
#               image_info_list       is a list of pairs of items, like this:
#                                        { name "foobar" } { filter "V" }
#                                        
#               keyword               is the keyword of desired value
#               
# Returns
#   the desired value            if the keyword exists
#   terminates                   if keyword not found
#  
proc get_image_value { image_info_list keyword } {
  global debug

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

  foreach pair $image_info_list {
    
    if { [llength $pair] != 2 } {
      puts stderr "get_image_value: pair $pair has length != 2"
      puts stderr " ABORTING"
      exit 1
    }
    set this_keyword [lindex $pair 0]
    set this_value [lindex $pair 1]
    if { [string compare $keyword $this_keyword] == 0 } {
      return $this_value
    }

  }

  # if we get here, was a bad keyword, not in list
  puts stderr "get_image_value: no keyword $keyword in following list"
  puts stderr "   $image_info_list"
  puts stderr " ABORTING"
  exit 1
}


############################################################################
# PROCEDURE: replace_image_value 
#
# DESCRIPTION: Given the name of an image, and a { keyword value } pair,
#              find the line describing the image in the image list file.  
#              Replace the value for the given keyword in the line,
#              and write a new version of the image list file with the
#              new line in it.
#
# RETURNS:     
#    0         if all goes well
#    1         if not
#
proc replace_image_value { image_name keyval_pair } {
  global debug
  variable List_File_Name
  variable List_File_Fields

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

  set list_file $List_File_Name
  if { [catch { set fid [open $list_file "r"] } ] != 0 } {
    puts stderr "replace_image_value: can't open file $list_file"
    return 1
  }
  set temp_file "./replace_image_value.tmp"
  if { [catch { set fout [open $temp_file "w"] } ] != 0 } {
    puts stderr "replace_image_value: can't open temp_file $temp_file"
    return 1
  }
  


  # we walk through the list file
  #   skip any lines which start with a pound sign
  #   skip (and print warning) for any lines which don't have the 
  #      expected format
  #      
  set i_list ""
  while { [gets $fid line] != -1 } {

    # send comments directly to the copy
    if { [regexp \#.* $line] == 1 } {
      puts $fout $line
      continue
    }

    # split line into pieces, expect exactly correct number of words
    if { [llength $line] != [llength $List_File_Fields] } {
      puts stderr "replace_image_value: bad line in file $list_file, will skip"
      puts stderr $line
      continue
    }

    # okay, this looks like a good entry
    # does the image name match?  If not, just send the line to the 
    #   output file as-is
    set name_index [lsearch $List_File_Fields "name"]
    if { [string compare [lindex $line $name_index] $image_name] != 0 } {
      puts $fout $line
      continue
    }
      
    # This line must describe the image of interest.
    #   Parse it, and build up the new version of the line,
    #   replacing the appropriate parameter with the new value
    set key_name [lindex $keyval_pair 0]
    set key_val  [lindex $keyval_pair 1]
    set newline ""

    set item_index 0
    while { $item_index < [llength $line] } {
      set this_field [lindex $List_File_Fields $item_index]
      set output_val [lindex $line  $item_index]
      if { [string compare $this_field $key_name] == 0 } {
        set output_val $key_val
      }
      lappend newline $output_val
      incr item_index
    }

    if { $debug > 3 } {
      puts "oldline is ..$line.."
      puts "newline is ..$newline.. "
    }
 
    puts $fout $newline
        
  }

  # all done
  close $fout
  close $fid

  # Now, replace the original "make_list.out" file with the modified version.
  #   if we can't overwrite the file, print an error and return with error
  set ret [catch { exec mv -f $temp_file $list_file } ]
  if { $ret != 0 } {
    puts stderr "replace_image_value: can't overwrite image list file $list_file"
    return 1
  }

  return 0
}



############################################################################
# PROCEDURE: designate_bad 
#
# DESCRIPTION: Given the name of an image, decide if we should mark
#              it as "bad", so that it is skipped in all subsequent
#              processing.  Look at the static variable "Skip_Bad_Images"
#
#                if Skip_Bad_Images = 1,   then set include field to zero
#                if Skip_Bad_Images = 0,   then do nothing
#               
# RETURNS:     
#    0         if all goes well
#    1         if an error occurs
#
proc designate_bad { image_name } {
  global debug
  variable Skip_Bad_Images

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

  if { $debug > 0 } {
    puts "designate_bad: Skip_Bad_Images is $Skip_Bad_Images ... "
    if { $Skip_Bad_Images == 0 } {
      puts "        .... so we will process even the bad images "
    } else {
      puts "        .... so we will skip over the bad images "
    }
  }
  if { $Skip_Bad_Images == 1 } {
    set klist [list "include" 0]
    if { [replace_image_value $image_name $klist] != 0 } {
      puts stderr "designate_bad: replace_image_value fails for $image_name"
      return 1
    }
  }

  return 0
}



############################################################################
# PROCEDURE: image_include_check 
#
# DESCRIPTION: Given the name of an image, check to see if its "include"
#              value is 1.  That means we should process it.  If the value
#              is 0, we should skip it.
#
# RETURNS:     
#    0         if we should skip image
#    1         if we should include image
#
proc image_include_check { image_name } {
  global debug

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

  if { [single_image_info $image_name image_info_list] != 0 } {
    puts stderr "image_include_check: single_image_info fails for $image_name"
    exit 1
  }
  set include [get_image_value $image_info_list "include"]
  if { $include == 0 } {
    return 0
  } else {
    return 1
  }

  # should never get here ....
  puts stderr "image_include_check: drop through for image $image_name ?!"
  exit 1
}




 # end of the namespace Proc_File_List
}
