#/bin/bash
#
# enable verbose debug (may be first line of script)
# set -x
# disable verbose
# set -

let num_ext=0
declare -a CHARACTER_LIST
declare -a PARAMETER_LIST

pauseScript()
{
   echo -en "\nHit return to continue "
   read RESP
   return
} # end pauseScript() #

quitScript()
{
   echo -en "\nHit return to EXIT "
   read RESP
   exit
} # end quitScript() #

readOptionFile()
{
  let num_ext=0
  unset CHARACTER_LIST
  unset PARAMETER_LIST
  LOCALHOST=`/bin/hostname | /bin/cut -d'.' -f1`

  if [ -f ${VER_CONFIG_FILE} ]; then
    echo "Begin Read Config File: ${VER_CONFIG_FILE}"
    for x in `/bin/cat ${VER_CONFIG_FILE} | grep -v "^ *#" | sed "s/  */;/g"`; do
    {
      if [ "${DEBUG}" -ge "2" ]; then
         echo "  FLine: x=>${x}<"
      fi
      optn=`echo ${x} | /bin/cut -d';' -f1`
      val1=`echo ${x} | /bin/cut -d';' -f2`
      val2=`echo ${x} | /bin/cut -d';' -f3`
      #val3=`echo ${x} | /bin/cut -d';' -f4`
      #val4=`echo ${x} | /bin/cut -d';' -f5`
      if [ "${DEBUG}" -ge "2" ]; then
         echo "         optn=>${optn}<"
         echo "         val1=>${val1}<"
         echo "         val2=>${val2}<"
         #echo "         val3=>${val3}<"
         #echo "         val4=>${val4}<"
      fi
      case ${optn} in
        VER_IDENTIFIER_STRING )
            echo "  VER_IDENTIFIER_STRING      = ${val1}"
            eval VER_IDENTIFIER_STRING=${val1}
            ;;
        VER_DATE_STRING ) 
            echo "  VER_DATE_STRING            = ${val1}_${val2}"
            eval 'VER_DATE_STRING=${val1}_${val2}'
            ;;
        VER_BUILDER_UID_STRING )
            echo "  VER_BUILDER_UID_STRING     = ${val1}"
            eval VER_BUILDER_UID_STRING=${val1}
            ;;
        VER_BUILD_HOST_STRING )
            echo "  VER_BUILD_HOST_STRING      = ${val1}"
            eval VER_BUILD_HOST_STRING=${val1}
            ;;
        VER_BUILD_DIR_PATH_STRING )
            echo "  VER_BUILD_DIR_PATH_STRING  = ${val1}"
            eval VER_BUILD_DIR_PATH_STRING=${val1}
            ;;
        VER_MD5_HASH_STRING )
            echo "  VER_MD5_HASH_STRING        = ${val1}"
            eval VER_MD5_HASH_STRING=${val1}
            ;;
        VER_GENERATE_HEADER )
            echo "  GENERATE_HEADER            = ${val1}"
            eval GENERATE_HEADER=${val1}
            ;;
        VER_DEBUG )
            echo "  DEBUG                      = ${val1}"
            eval DEBUG=${val1}
            ;;
        VER_EXTENDED )
            chr="_`echo ${val1} | cut -c1`______"
            #echo "  VER_EXTENDED               = ${val1} ${val2}"
            let num_ext++
            CHARACTER_LIST[${num_ext}]="${chr}"
            PARAMETER_LIST[${num_ext}]="${val2}"
            echo "  VER_EXTENDED[${chr}]     = ${val2}"
            ;;
        *   )
            echo "Error: read ${x}"
            ;;
      esac
    } done  # end loop through file content #
  fi   # end if ${VER_CONFIG_FILE} exists #
} # end readOptionFile() #


getVersionValues()
{
   ##
   ## Lowest Priority
   ## 1) Check and pupulate default environment variables first
   ##    or use defaults, as follows
   ##
   if [ "x${VER_DEBUG}" == "x" ]; then
      DEBUG=0
   else
      DEBUG=${VER_DEBUG}
   fi
   if [ "x${VER_CONFIG_FILE}" == "x" ]; then
      # FIXME : TODO  probably bad default, should use a local directory option??
      VER_CONFIG_FILE=${0}.config
   fi
   if [ "x${VER_IDENTIFIER_STRING}" == "x" ]; then
      VER_IDENTIFIER_STRING=Undefined_Identifier
   fi
   if [ "x${VER_DATE_STRING}" == "x" ]; then
      VER_DATE_STRING=`date "+%Y-%m-%d_%H:%M:%S"`
   fi
   if [ "x${VER_BUILDER_UID_STRING}" == "x" ]; then
      VER_BUILDER_UID_STRING=${USERNAME}
   fi
   if [ "x${VER_BUILD_HOST_STRING}" == "x" ]; then
      VER_BUILD_HOST_STRING=`/bin/hostname | /bin/cut -d'.' -f1`
   fi
   if [ "x${VER_BUILD_DIR_PATH_STRING}" == "x" ]; then
      VER_BUILD_DIR_PATH_STRING=`pwd -P`
   fi
   if [ "x${VER_MD5_HASH_STRING}" == "x" ]; then
      VER_MD5_HASH_STRING=Undefined_MD5_Hash
   fi
   if [ "x${VER_GENERATE_HEADER}" == "x" ]; then
      GENERATE_HEADER=0
   else
      GENERATE_HEADER=${VER_GENERATE_HEADER}
   fi
    

   ##
   ## Second Hightest Priority
   ## 2) if ${VER_CONFIG_FILE} exists, parse it
   ##    potentially overwriting Environment variables and defaults
   if [ -f ${VER_CONFIG_FILE} ]; then
      # read version strings from config file
      readOptionFile
   fi


   ##
   ## Hightest Priority
   ## 3) Parse command line options, later command options override earlier options
   ##
   echo "Begin Parse arguments: ${ARGUEMENTS}"
   while getopts ":C:S:D:U:H:P:M:d:hg" opt ${ARGUEMENTS}; do
      if [ "${DEBUG}" -ge "2" ]; then
         echo "  OLine: opt =>${opt}<"
         echo "         OPTARG=>${OPTARG}<"
      fi
      case ${opt} in
        C ) if [ "${DEBUG}" -ge "1" ]; then
               echo "  VER_CONFIG_FILE            =${OPTARG}"
            fi
            eval VER_CONFIG_FILE=${OPTARG}
            # use this, read the config file now!
            readOptionFile
            ;;
        S ) if [ "${DEBUG}" -ge "1" ]; then
               echo "  VER_IDENTIFIER_STRING      =${OPTARG}"
            fi
            eval VER_IDENTIFIER_STRING=${OPTARG}
            ;;
        D ) if [ "${DEBUG}" -ge "1" ]; then
               echo "  VER_DATE_STRING            =${OPTARG}"
            fi
            eval VER_DATE_STRING=${OPTARG}
            ;;
        U ) if [ "${DEBUG}" -ge "1" ]; then
               echo "  VER_BUILDER_UID_STRING     =${OPTARG}"
            fi
            eval VER_BUILDER_UID_STRING=${OPTARG}
            ;;
        H ) if [ "${DEBUG}" -ge "1" ]; then
               echo "  VER_BUILD_HOST_STRING      =${OPTARG}"
            fi
            eval VER_BUILD_HOST_STRING=${OPTARG}
            ;;
        P ) if [ "${DEBUG}" -ge "1" ]; then
               echo "  VER_BUILD_DIR_PATH_STRING  =${OPTARG}"
            fi
            eval VER_BUILD_DIR_PATH_STRING=${OPTARG}
            ;;
        M ) if [ "${DEBUG}" -ge "1" ]; then
               echo "  VER_MD5_HASH_STRING        =${OPTARG}"
            fi
            eval VER_MD5_HASH_STRING=${OPTARG}
            ;;
        g ) if [ "${DEBUG}" -ge "1" ]; then
               echo "  GENERATE_HEADER            =1"
            fi
            eval GENERATE_HEADER=1
            ;;
        d ) if [ "${DEBUG}" -ge "1" ]; then
               echo "  DEBUG                      =${OPTARG}"
            fi
            eval DEBUG=${OPTARG}
            ;;
        h ) echo "usage: bld_version [-C <config_file>] [-S <identifier_str>] [-D <date_str>]"
            echo "                   [-U <build_user_id>] [-H <build_hostname>] [-P <build_path>]"
            echo "                   [-M <md5_hash_str>] [-d <debug_value>] [-h ] [-g]"
            echo "  Parse supplied command line options:"
            echo "    -C VER_CONFIG_FILE : use text file to read in version strings"
            echo "    -S VER_IDENTIFIER_STRING"
            echo "    -D VER_DATE_STRING"
            echo "    -U VER_BUILDER_UID_STRING"
            echo "    -H VER_BUILD_HOST_STRING"
            echo "    -P VER_BUILD_DIR_PATH_STRING"
            echo "    -M VER_MD5_HASH_STRING"
            echo "    -g Generate corresponding: bld_version.hpp"
            echo "    -d DEBUG value : 0=off, 1=normal, 2=verbose"
            echo "    -h print this help text"
            echo "  Environment variables (and Config File tokens):"
            echo "    VER_CONFIG_FILE"
            echo "    VER_IDENTIFIER_STRING"
            echo "    VER_DATE_STRING"
            echo "    VER_BUILDER_UID_STRING"
            echo "    VER_BUILD_HOST_STRING"
            echo "    VER_BUILD_DIR_PATH_STRING"
            echo "    VER_MD5_HASH_STRING"
            echo "    VER_GENERATE_HEADER"
            echo "    VER_DEBUG"
            echo "  Configuration File Only token:"
            echo "    VER_EXTENDED    <char> <value>"
            quitScript
            ;;
       \? ) echo "Invalid option: -${OPTARG}" 1>&2
            ;;
        : ) echo "Invalid option: -${OPTARG} requires an argument" 1>&2
            ;;
      esac
   done # parse options #
   #shift $((OPTIND-1))
   #echo "END Parse arguments: ${ARGUEMENTS}"
} # end getVersionValues() #


verifyExtended()
{
   # need to verify there are no duplicate variables to cause a compile error
   #    _pver_extra${CHARACTER_LIST[${lcv}]}
   #    _ver_extra${CHARACTER_LIST[${lcv}]}
   if [ "${DEBUG}" -ge "2" ]; then
      echo "begin verifyExtended"
   fi
   let oLcv=1
   while [ "${oLcv}" -le "${num_ext}" ]; do
      #echo "oLcv = ${oLcv}"
      let "iLcv=${oLcv} + 1"
      while [ "${iLcv}" -le "${num_ext}" ]; do
         #echo "iLcv = ${iLcv}"
         if [ "${oLcv}" -ne "${iLcv}" ]; then
            if [ "${DEBUG}" -ge "2" ]; then
               echo " compare: ${CHARACTER_LIST[${oLcv}]} to ${CHARACTER_LIST[${iLcv}]}"
            fi
            # if these are equal, print error and terminate
            if [ "${CHARACTER_LIST[${oLcv}]}" == "${CHARACTER_LIST[${iLcv}]}" ]; then
               echo " Error: ${PARAMETER_LIST[${oLcv}]} and ${PARAMERER_LIST[${iLcv}]} both use ${CHARACTER_LIST[${iLcv}]}"
               quitScript
            fi
         fi  #  don't compare an index to itself
         let iLcv++
      done
      let oLcv++
   done
} # end verifyExtended() #



printVersionValues()
{
   echo " VER_CONFIG_FILE            = ${VER_CONFIG_FILE}"
   echo " VER_IDENTIFIER_STRING      = ${VER_IDENTIFIER_STRING}"
   echo " VER_DATE_STRING            = ${VER_DATE_STRING}"
   echo " VER_BUILDER_UID_STRING     = ${VER_BUILDER_UID_STRING}"
   echo " VER_BUILD_HOST_STRING      = ${VER_BUILD_HOST_STRING}"
   echo " VER_BUILD_DIR_PATH_STRING  = ${VER_BUILD_DIR_PATH_STRING}"
   echo " VER_MD5_HASH_STRING        = ${VER_MD5_HASH_STRING}"
   echo " GENERATE_HEADER            = ${GENERATE_HEADER}"
   echo " DEBUG                      = ${DEBUG}"
   let lcv=1
   while [ "${lcv}" -le "${num_ext}" ]; do
      echo " ${CHARACTER_LIST[${lcv}]}                   = ${PARAMETER_LIST[${lcv}]}"
      let lcv++
   done
} # end printVersionValues() #


generateHpp()
{
   OUTFILE=bld_version.hpp
   GEN_TIME=`date "+%Y-%m-%d_%k:%M:%S"`
   GEN_USER=${USERNAME}

   echo -e "/*" > ${OUTFILE}
   echo -e " * bld_version.h" >> ${OUTFILE}
   echo -e " *" >> ${OUTFILE}
   echo -e " *  Generated: ${GEN_TIME} by ${GEN_USER}" >> ${OUTFILE}
   echo -e " */" >> ${OUTFILE}
   echo -e "" >> ${OUTFILE}
   echo -e "#ifndef BLD_VERSION_H_" >> ${OUTFILE}
   echo -e "#define BLD_VERSION_H_" >> ${OUTFILE}
   echo -e "" >> ${OUTFILE}
   echo -e "#include <string>" >> ${OUTFILE}
   echo -e "#include <vector>" >> ${OUTFILE}
   echo -e "using namespace std;" >> ${OUTFILE}
   echo -e "" >> ${OUTFILE}
   echo -e "// _ver_string might be the branch||tag, otherwise add branch||tag" >> ${OUTFILE}
   echo -e "extern const char* _ver_string;" >> ${OUTFILE}
   echo -e "extern const char* _ver_date;" >> ${OUTFILE}
   echo -e "extern const char* _ver_build_uid;" >> ${OUTFILE}
   echo -e "extern const char* _ver_host;" >> ${OUTFILE}
   echo -e "extern const char* _ver_path;" >> ${OUTFILE}
   echo -e "extern const char* _ver_md5;" >> ${OUTFILE}
   echo -e "" >> ${OUTFILE}
   echo -e "// accessors to provide version strings for code access" >> ${OUTFILE}
   echo -e "" >> ${OUTFILE}
   echo -e "inline const string ver_string() {return string (_ver_string);}" >> ${OUTFILE}
   echo -e "inline const string ver_date() {return string (_ver_date);}" >> ${OUTFILE}
   echo -e "inline const string ver_build_uid() {return string (_ver_build_uid);}" >> ${OUTFILE}
   echo -e "inline const string ver_host() {return string (_ver_host);}" >> ${OUTFILE}
   echo -e "inline const string ver_path() {return string (_ver_path);}" >> ${OUTFILE}
   echo -e "inline const string ver_md5() {return string (_ver_md5);}" >> ${OUTFILE}
   echo -e "" >> ${OUTFILE}
   echo -e "const vector <string> ver_string_list();" >> ${OUTFILE}
   echo -e "#endif /* BLD_VERSION_H_ */" >> ${OUTFILE}
} # end generateHpp() #


generateCpp()
{
   OUTFILE=bld_version.cpp
   GEN_TIME=`date "+%Y-%m-%d_%k:%M:%S"`
   GEN_USER=${USERNAME}

   echo -e "/*" >  ${OUTFILE}
   echo -e " * bld_version.cpp" >> ${OUTFILE}
   echo -e " *" >> ${OUTFILE}
   echo -e " *  Generated: ${GEN_TIME} by ${GEN_USER}" >> ${OUTFILE}
   echo -e " *" >> ${OUTFILE}
   echo -e " *  auto generated content, do not modify" >> ${OUTFILE}
   echo -e " *  more description of overall approach" >> ${OUTFILE}
   echo -e " *" >> ${OUTFILE}
   echo -e " */" >> ${OUTFILE}
   echo -e "" >> ${OUTFILE}
   echo -e "#include \"bld_version.hpp\"" >> ${OUTFILE}
   echo -e "" >> ${OUTFILE}
   echo -e "/*" >> ${OUTFILE}
   echo -e " * any compiler directives to retain unused variables" >> ${OUTFILE}
   echo -e " * #DEFINE dont_optimize_away" >> ${OUTFILE}
   echo -e " *" >> ${OUTFILE}
   echo -e " * use unique character pattern to search for version strings:" >> ${OUTFILE}
   echo -e " * strings <executable> | grep \"^_.______\" | cut -c9-" >> ${OUTFILE}
   echo -e " */" >> ${OUTFILE}
   echo -e "const char* _pver_string    = \"_s______${VER_IDENTIFIER_STRING}\";" >> ${OUTFILE}
   echo -e "const char* _pver_date      = \"_d______${VER_DATE_STRING}\";" >> ${OUTFILE}
   echo -e "const char* _pver_build_uid = \"_u______${VER_BUILDER_UID_STRING}\";" >> ${OUTFILE}
   echo -e "const char* _pver_host      = \"_h______${VER_BUILD_HOST_STRING}\";" >> ${OUTFILE}
   echo -e "const char* _pver_path      = \"_p______${VER_BUILD_DIR_PATH_STRING}\";" >> ${OUTFILE}
   echo -e "const char* _pver_md5       = \"_m______${VER_MD5_HASH_STRING}\";" >> ${OUTFILE}
   #---------------------------------------------------
   let lcv=1
   while [ "${lcv}" -le "${num_ext}" ]; do
      echo -e "const char* _pver_extra${CHARACTER_LIST[${lcv}]} = \"${CHARACTER_LIST[${lcv}]}${PARAMETER_LIST[${lcv}]}\";" >> ${OUTFILE}
      let lcv++
   done
   #---------------------------------------------------
   #echo -e "const char* _pver_extra1    = \"_1______arbitrary extra string 1\";" >> ${OUTFILE}
   #echo -e "const char* _pver_extra2    = \"_2______arbitrary extra string 2\";" >> ${OUTFILE}
   echo -e "" >> ${OUTFILE}
   echo -e "const char* _ver_string     = (char*)(&_pver_string[8]);" >> ${OUTFILE}
   echo -e "const char* _ver_date       = (char*)(&_pver_date[8]);" >> ${OUTFILE}
   echo -e "const char* _ver_build_uid  = (char*)(&_pver_build_uid[8]);" >> ${OUTFILE}
   echo -e "const char* _ver_host       = (char*)(&_pver_host[8]);" >> ${OUTFILE}
   echo -e "const char* _ver_path       = (char*)(&_pver_path[8]);" >> ${OUTFILE}
   echo -e "const char* _ver_md5        = (char*)(&_pver_md5[8]);" >> ${OUTFILE}
   #---------------------------------------------------
   let lcv=1
   while [ "${lcv}" -le "${num_ext}" ]; do
      echo -e "const char* _ver_extra${CHARACTER_LIST[${lcv}]} = (char*)(&_pver_extra${CHARACTER_LIST[${lcv}]}[8]);" >> ${OUTFILE}
      let lcv++
   done
   #---------------------------------------------------
   #echo -e "const char* _ver_extra1     = (char*)(&_pver_extra1[8]);" >> ${OUTFILE}
   #echo -e "const char* _ver_extra2     = (char*)(&_pver_extra2[8]);" >> ${OUTFILE}
   echo -e "" >> ${OUTFILE}
   echo -e "" >> ${OUTFILE}
   echo -e "const vector <string> ver_string_list()" >> ${OUTFILE}
   echo -e "{" >> ${OUTFILE}
   echo -e "    vector <string> local_vector;" >> ${OUTFILE}
   echo -e "    local_vector.push_back(string (_ver_string));" >> ${OUTFILE}
   echo -e "    local_vector.push_back(string (_ver_date));" >> ${OUTFILE}
   echo -e "    local_vector.push_back(string (_ver_build_uid));" >> ${OUTFILE}
   echo -e "    local_vector.push_back(string (_ver_host));" >> ${OUTFILE}
   echo -e "    local_vector.push_back(string (_ver_path));" >> ${OUTFILE}
   echo -e "    local_vector.push_back(string (_ver_md5));" >> ${OUTFILE}
   #---------------------------------------------------
   let lcv=1
   while [ "${lcv}" -le "${num_ext}" ]; do
      echo -e "    local_vector.push_back(string (_ver_extra${CHARACTER_LIST[${lcv}]}));" >> ${OUTFILE}
      let lcv++
   done
   #---------------------------------------------------
   #echo -e "    local_vector.push_back(string (_ver_extra1));" >> ${OUTFILE}
   #echo -e "    local_vector.push_back(string (_ver_extra2));" >> ${OUTFILE}
   echo -e "    return local_vector;" >> ${OUTFILE}
   echo -e "}" >> ${OUTFILE}
} # end generateCpp() #



##############################################################################
##
##              Begin Main Processing 
##
##############################################################################

ARGUEMENTS=$@
echo -e "\n--------------------------------------------------------"
echo "Command: ${0} $@"
echo -e "--------------------------------------------------------"
getVersionValues
if [ "${DEBUG}" -ge "2" ]; then
   printVersionValues
fi

# need to ensure operator has not duplicated any characters in the VER_EXTENDED
# or that will cause a compile error
verifyExtended
generateCpp

if [ "x${GENERATE_HEADER}" == "x1" ]; then
   generateHpp
fi


##
## perhaps update config file for next run
##
if [ "x" == "y" ]; then
   # FIXME: TBD logic
   # if ver_config_file exists
   echo "Updating: ${VER_CONFIG_FILE}"
   # may need to re-read options, and perhaps update certain ones (not DATE, but IDENTIFIER...)
   # need to know which options were read_in that have changed...
   sed -i "s,^ *VER_IDENTIFIER_STRING*$,VER_IDENTIFIER_STRING  ${VER_IDENTIFIER_STRING}," ${VER_CONFIG_FILE}
fi


