#
# A simple procedure which sets/returns the current version
#   of the pipeline code.
#   -- 04/12/2003  MWR
#
# We now make a number of additional checks of the version numbers
#   of the auxiliary packages.  Moved the procedure which checks
#   for compatibility from "setup.tcl" to this file.
# In addition to keeping track of the version of the pipeline
#   source code itself, we also now keep track of the versions
#   of all the software packages.
#   -- 04/22/2003  MWR

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



#############################################################################
# PROCEDURE: check_compatibility
#
# DESCRIPTION: Check to see that some packages have the proper version
#              to run against this pipeline.  We look at two numbers
#              for each package:
#
#                   the actual version of the package (derived via
#                           something like "match --version" in some cases)
#
#                   the version which is required, as written in the
#                           version.param file
#
#              If the actual version is greater than or equal to the
#              required version, all is well.  But if not, we 
#              print error messages and return with an error.
#
# USAGE:       check_compatibility  driver_param
#
#              where "driver_param" is the name of the "driver" param
#              file, which should have a list of all the param files
#              used in the pipeline.  We must scan through some of these
#              other param files to find the executables which yield
#              the actual package version numbers.
#
# RETURNS:
#     0                    if all goes well
#     1                    if an error occurs 
#
proc check_compatibility { driver_param } {
  global debug 

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

  set ret_code 0

  # we'll need the name of the "setup" param file later on, so get it now
  set setup_param [get_param $driver_param "setup_param"]

  # first, we get the name of the "version" param file, in which we will
  #   find a list of packages to check
  if { [file exists $driver_param] != 1 } {
    puts stderr "check_compatibility: can't open driver param $driver_param"
    return 1
  }
  set version_param [get_param $driver_param "version_param"]
  if { $debug > 2 } {
    puts "check_compatibility: version param file is $version_param"
  }

  # now, in this version param file, we look for a list of the packages
  #   which we need to check for compatibility.  We expect to find a 
  #   line which looks like this:
  #
  #     check_packages  { match photom xvista }
  # 
  if { [file exists $version_param] != 1 } {
    puts stderr "check_compatibility: can't open param $version_param"
    return 1
  }
  set package_list  [get_param $version_param "check_packages"]
  if { $debug > 2 } {
    puts "check_compatibility: package_list is $package_list"
  }

  # We assume that somewhere in the list of all param files we can find
  #   two values for each of these packages:
  #
  #      a. a directory containing an executable; the name of this
  #         parameter will be the name of the package with a suffix "_dir",
  #         like so:
  # 
  #               match    -->   match_dir
  #               photom   -->   photom_dir
  #
  #      b. the name of an executable file which will yield the
  #         version of the package when executed with --version.
  #         The name of this parameter will be the name of the package
  #         with the suffix "_exe", like so:
  #
  #               match    -->   match_exe
  #               photom   -->   photom_exe
  #
  foreach package $package_list {
    if { $debug > 1 } { 
      puts "check_compatibility: next package is ..$package.."
    }

    # split the information in the param file into the package name
    #   and required version
    if { [llength $package] != 2 } {
      puts stderr "check_compatibility: package $package has bad format"
      return 1
    } 
    set package_name [lindex $package 0]
    set package_req  [lindex $package 1]
    if { $debug > 2 } {
      puts "check_compatibility: name $package_name req $package_req"
    }

    # get the directory containing the executable from the 
    #   "setup" parameter file.  We expect to see a parameter with
    #   name like "foo_dir" if "foo" is the package name
    set dir_param [format "%s_dir" $package_name]
    if { [catch { set dir [get_param $setup_param $dir_param] } ] != 0 } {
      puts stderr "check_compatibility: can't read dir for $package_name"
      return 1
    }
    if { $debug > 2 } {
      puts "check_compatibility: $package_name has dir $dir"
    }

    # get the name of the executable for this package, which is linked
    #  to a parameter like "foo_exe" if "foo" is the package_name
    #  This is tricky, because it might be in _any_ of the parameter files,
    #  so we have to check them all.
    if { [get_exec_name $driver_param $package_name exec_name] != 0 } {
      puts stderr "check_compatibility: get_exec_name fails for $package_name"
      return 1
    }

    # Okay, so we now have the directory in which the executable
    #   program lives, and we have the name of the exectuble.
    #   We can call the executable program to find out what version
    #   it is.
    if { [get_exec_version $dir $exec_name version] != 0 } {
      puts stderr "check_compatibility: get_exec_version fails for $package_name"
      return 1
    }

    # Now compare the actual version against the required version
    if { $version < $package_req } {
      puts "check_compatibility: $package_name req $package_req, actual $version"
      incr ret_code
    }

    # Hooray!  We found a valid version.  Let's add a { package version }
    #   pair to the list of all packages, so we can print it out
    #   later on.
    if { [append_version_list $package_name $version] != 0 } {
      puts stderr "check_compatibility: append_version_list fails for $package_name"
      return 1
    }

  }

  return $ret_code
}



############################################################################
# PROCEDURE: get_exec_name
#
# DESCRIPTION: Search through all the param files listed in the 
#              given driver file for a parameter with a name
#              like "foo_exe".  Place the value associated with
#              that parameter into the final argument.
#
# RETURNS
#     0     if all goes well
#     1     if an error occurs
#
proc get_exec_name { driver_param package_name o_exec_name } {
  upvar $o_exec_name exec_name
  global debug

  set exec_name {}

  if { $debug > 2 } {
    puts "entering get_exec_name"
  }

  # this is just a sanity check
  if { [file exists $driver_param] != 1 } {
    puts stderr "get_exec_name: file $driver_param doesn't exist?"
    return 1
  }

  # walk through the driver param file, looking for lines which
  #   are of the form
  #              setup_param     setup.param
  #              list_param      make_list.param
  #  
  # for each one that we find, we search THAT param file for
  # a parameter with a name like "foo_exe", if "foo" is the 
  # package name
  set search_for_name [format "%s_exe" $package_name]
  if { $debug > 2 } {
    puts "get_exec_name: looking for keyword $search_for_name"
  }
  set fid [open $driver_param "r"]
  if { $fid < 0 } {
    puts stderr "get_exec_name: can't open file $driver_param"
    return 1
  }
  while { [gets $fid line] != -1 } {
    if { [llength $line] != 2 } {
      continue
    }
    set keyword [lindex $line 0]
    if { [string match *_param $keyword] == 1 } {
      set this_param_file [lindex $line 1]
      if { $debug > 2 } {
        puts "get_exec_name: about to search $this_param_file"
      }

      # okay, we found a param file.  Search it for the _exe keyword
      set exec_name [get_param $this_param_file "$search_for_name"] 
      if { $exec_name != {} } {
        if { $debug > 2 } {
          puts "get_exec_name: found it!  exec_name is $exec_name"
        }
        break
      }
    }
  }
  close $fid

  # did we find a name for the executable?
  if { $exec_name != {} } {
    return 0
  } else {
    return 1
  }

}



############################################################################
# PROCEDURE: get_exec_version
#
# DESCRIPTION: Given the directory and name of an external executable
#              program, build a command to execute the program
#              with the --version argument.  We expect that to return
#              a string like this:
#
#                     package 1.2
#
#              Parse this and get the numerical value which
#              ought to be the second element.  Place this numerical
#              value into the output "o_version" argument.
#
# RETURNS
#     0     if all goes well
#     1     if an error occurs
#
proc get_exec_version { dir exec_name o_version } {
  upvar $o_version version
  global debug

  set version {}

  if { $debug > 2 } {
    puts "entering get_exec_version"
  }

  # build the command to call the program with --version arg
  set cmd [format "exec %s/%s --version" $dir $exec_name]
  if { $debug > 2 } {
    puts "get_exec_version: cmd is ..$cmd.."
  }
  if { [catch { set retval [eval $cmd] }] != 0 } {
    puts stderr "get_exec_version: eval of cmd ..$cmd.. fails"
    return 1
  }
  if { $debug > 2 } {
    puts "get_exec_version: retval is $retval"
  }

  # we expect this returned string to look like 
  #        program version 0.2
  #   with the numerical value as the final argument
  set len [llength $retval]
  if { $len < 2 } {
    puts stderr "get_exec_version: retval from ..$cmd.. too short"
    return 1
  }
 
  set version [lindex $retval [expr $len-1] ]
  # check to make sure that the version is numeric
  if { [scan $version "%f" dummy] != 1 } {
    puts stderr "get_exec_version: final elem $version is not numeric"
    return 1
  }

  if { $debug > 2 } {
    puts "get_exec_version: version is $version"
  }
 
  return 0
}

  
############################################################################
# PROCEDURE: concat_all_params
#
# DESCRIPTION: Create a single large file which is the 
#              concatenation of all the param files listed in the 
#              given driver file.  Save this big file
#              in the given directory with the given stem and
#              an extension ".param"
#
# RETURNS
#     0     if all goes well
#     1     if an error occurs
#
proc concat_all_params { driver_param } {
  global debug

  if { $debug > 1 } {
    puts "entering concat_all_params"
  }

  # this is just a sanity check
  if { [file exists $driver_param] != 1 } {
    puts stderr "concat_all_params: file $driver_param doesn't exist?"
    return 1
  }

  # we use the "version_param" file to get the output_dir
  set version_param [get_param $driver_param "version_param"]
  if { $debug > 2 } {
    puts "concat_all_params: version param file is $version_param"
  }
  if { [file exists $version_param] != 1 } {
    puts stderr "concat_all_params: can't open param $version_param"
    return 1
  }
  set output_dir  [get_param $version_param "output_dir"]


  # get the stem of the name (which has been set previously
  #   by the "photom" step, we hope)
  set concat_stem [get_photom_stem]
  if { $concat_stem == {} } { 
    puts stderr "concat_all_params: the stem name is empty?"
    return 1
  }

  # create the full path name of the output file
  set output_name [format "%s/%s.param" $output_dir $concat_stem]
  if { $debug > 2 } {
    puts "concat_all_params: concat_stem is is $concat_stem"
  }

  # walk through the driver param file, looking for lines which
  #   are of the form
  #              setup_param     setup.param
  #              list_param      make_list.param
  #  
  #   Append the contents of each one to the growing output file.
  set fid [open $driver_param "r"]
  if { $fid < 0 } {
    puts stderr "concat_all_params: can't open file $driver_param"
    return 1
  }
  set first 1
  while { [gets $fid line] != -1 } {
    if { [llength $line] != 2 } {
      continue
    }
    set keyword [lindex $line 0]
    if { [string match *_param $keyword] == 1 } {
      set this_param_file [lindex $line 1]
      if { $debug > 2 } {
        puts "concat_all_params: about to append $this_param_file"
      }

      # okay, we found a param file.  Append it to the growing output file
      if { $first == 1 } {
        set first 0
        set cmd "exec echo \"# next is file $this_param_file\" > $output_name"
      } else {
        set cmd "exec echo \"# next is file $this_param_file\" >> $output_name"
      }
      if { $debug > 2 } {
        puts "cmd is ..$cmd.."
      }
      if { [catch { eval $cmd } ] != 0 } {
        puts "concat_all_params: error on echo for $this_param_file"
        return 1
      }
      set cmd "exec cat $this_param_file >> $output_name"
      if { $debug > 2 } {
        puts "cmd is ..$cmd.."
      }
      if { [catch { eval $cmd } ] != 0 } {
        puts "concat_all_params: error on cat for $this_param_file"
        return 1
      }
      set cmd "exec echo \"# end of file $this_param_file\" >> $output_name"
      if { $debug > 2 } {
        puts "cmd is ..$cmd.."
      }
      if { [catch { eval $cmd } ] != 0 } {
        puts "concat_all_params: error on echo end for $this_param_file"
        return 1
      }
      set cmd "exec echo \"#####################################################\" >> $output_name"
      if { $debug > 2 } {
        puts "cmd is ..$cmd.."
      }
      if { [catch { eval $cmd } ] != 0 } {
        puts "concat_all_params: error on echo end for $this_param_file"
        return 1
      }
    }
  }
  close $fid

  return 0

}




namespace eval Version {
  namespace export init_version get_version \
                         append_version_list get_version_list
  variable Version_Number {}
  variable Version_List {}


###########################################################################
# Read the value of the pipeline version from the given param file,
#   and save it in a private variable for later use.
#   We also initialize the Version_List variable to 
#                 { pipeline 0.2 }
#   or whatever.
#
# Args:
#      param_file         name of file containing the version number
#
# RETURNS
#      0         if all goes well
#      1         if there's a problem
#    
proc init_version { param_file } {
  global debug
  variable Version_Number
  variable Version_List

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

  # get information from the param file
  if { [file exists $param_file] != 1 } {
    puts stderr "version: can't open param file $param_file"
    return 1
  }
  set Version_Number [get_param $param_file "pipeline"]
puts "Version_Number is ..$Version_Number.."
  set Version_List [list [list "pipeline" $Version_Number] ]

  return 0
}


###########################################################################
# Return the value of the Version_Number variable
#
# RETURNS
#      Version_Number
#    
proc get_version { } {
  global debug
  variable Version_Number

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

  return $Version_Number 
}


###########################################################################
# Append a "package release" pair to the Version_List variable.  In other
#   words, turn
#
#           { pipeline 0.2 }
#
#   into
#           { pipeline 0.2 } { match 0.7 } 
#
#
# RETURNS
#      0         if all goes well
#      1         if there's a problem
#    
proc append_version_list { package release } {
  global debug
  variable Version_List

  if { $debug > 0 } {
    puts "entering append_version_list"
  }
  if { $debug > 0 } {
    puts "append_version_list: Version_List starts as ..$Version_List.."
  }

  lappend Version_List [list $package $release]
  if { $debug > 0 } {
    puts "append_version_list: Version_List is now ..$Version_List.."
  }

  return 0
}



###########################################################################
# Return the value of the Version_List variable
#
# RETURNS
#      Version_List
#    
proc get_version_list { } {
  global debug
  variable Version_List

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

  return $Version_List 
}


 # end of the namespace Version
}


