#!/bin/bash
#################################################################
#
#  File: ciao-install
#
#  Description
#  Script to assist users in downloading, installing and ciao.
#  Version 2.12
# 
#  Copyright (C) 2024 Smithsonian Astrophysical Observatory
#
#  This program is free software; you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation; either version 2 of the License, or
#  (at your option) any later version.
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License along
#  with this program; if not, write to the Free Software Foundation, Inc.,
#  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
#################################################################

export STARTDIR="$(pwd)"
export CONDA_PROG="conda"
export CALDB_VER="docs/chandra/caldb_version/caldb_version.fits"
export LOGFILE_NAME="ciao-install-$(date +%y-%m-%d.%H.%M.%S).log"
export CIAO_VER="4.17"

export INSTALLER_VER="${CIAO_VER}.0"
export BASE_URL="https://cxc.cfa.harvard.edu/ciao-install/ciao-${CIAO_VER}/"

export CONSTRUCTORS_DIR="conda_constructors"
export CONDA_INSTALLERS_DIR="conda_installers"
export SPEC_FILES_DIR="env_files"

#Defaults
#export segments="ciao sherpa marx contrib caldb acis hrc ciao-src sherpa-src"
export segments="ciao sherpa marx contrib caldb"
export special_segs="${segments}"
## The DEF_* Variables are over-written with values found in
## the CIAOINSTALLRC file. If that file does not exist,
## the DEF_* values are used.
DEF_DL_DIR="$(pwd)"       # Default Download Directory
DEF_INS_DIR="${HOME}"    # Default Install Directory
DEF_RUN_SMOKE="y"        # run smoke tests after install
DEF_DELETE_TAR="n"
export ciao_prefix=""
export ciao_cache=""
export pkg_cache=""
export env_files_path=""
export req_tests=""
export del_cache=""
export download_only=""
export download_all=""
export install_only=""
export fake_top="" 
export existing_caldb=""
export cross_platform="n"
export ins_type="construct"
export force=""
export verbose="n"
export logdir="${STARTDIR}"
export CIAOINSTALLRC="${HOME}/.ciaoinstall.rc"
TMPDIR="/tmp"            # Directory to write tmp files
TMPNAME="tmp-ciao-install-$$" # name of temporary files
WORKFILE="${TMPDIR}/${TMPNAME}"  # Workfile name
#Save ahelp indexing until after install complete
export SKIP_AHELP_INDEXING="y"

#Exit codes
export OK=0                      # No error
export DIR_NOT_SUPPLIED=2        # Directory not supplied
export DIR_INVALID=3             # Invalid directory
export UNKNOWN_ARGUMENT=4        # Invalid command line argument
export UNKNOWN_VERSION=5         # Unknown Version
export UNKNOWN_FILE=6            # Unknown file
export CALDB_NOT_FOUND=7         # User specified a CALDB directory that does not exist
export INSUFFICIENT_SPACE=8      # Not enough disk space
export INSTALL_FAILED=9          # Install failed
export CONDA_INVALID=10          # Invalid or failed Conda install
export DOWNLOAD_FAILED=11        # Failed to download the install products
export SPECS_INVALID=12          # Invalid specification file location
export LINK_CALDB_FAIL=13        # Failed to link CALDB (likely due to already being copied)

usage()
{
   echo "Usage: ${0} [options...]"
   echo
   echo "  -h --help          Print this message"
   echo "  --download-only    Download only (do not install)"
   echo "  --install-only     Install only (do not download)"
   echo "  --cache <dir>      Cache directory to use"
   echo "  --pkg-cache <dir>  (Optional) Override the default package cache"
   echo "  --prefix <dir>     Install directory"
   echo "  --link-caldb <dir> Location of CALDB to link in"
   echo "  --system <system>  System to install"
   echo "     (linux, macos, or macos-arm)"
   echo "  --delete-cache     Delete the download cache"
   echo "  --add <segment>    Add additional segment to CIAO"
   echo "  --remove <segment> Skip segment."
   echo "  --skip-tests       Skip tests after install"
   echo "  --small-files      Use smaller files to install where possible."
   echo "  --force            Overwrite existing install"
   echo "  --update           Update existing install"
   echo "  --with-top <dir>   (non-standard) Fix the init scripts"
   echo "                     to think they are elsewhere"
   echo "                     (eg. cluster install)"
   echo "  --download-all <dir> "
   echo "                     (non-standard) Generates a complete"
   echo "                     cache of all products required for install"
   echo "                     for all supported platforms."
   echo "                     Forces --download-only"
   echo "  --logdir <dir>     Log file directory (defaults to current dir)"
   echo "  -v, --verbose      Includes more progress output to the screen"
}

valid_dir()
{
   dir_loc="${1}"
   prev_dir="$(pwd)"

   if [ "x${dir_loc}" == "x" ] ; then
      echo "ERROR: Did not specify a directory."
      exit ${DIR_NOT_SUPPLIED}
   fi
   if [ ! -d "${dir_loc}" ] ; then
      mkdir -p ${dir_loc}
      if [ "$?" != "0" ] ; then
         echo "ERROR: Could not create the following directory"
         echo "   ${dir_loc}"
         exit ${DIR_INVALID}
      fi
   fi
   if [ ! -w "${dir_loc}" ] && [ "x${2}" != "xnowrite" ] ; then
      echo "ERROR: The following directory exists, but is not writeable."
      echo "   ${dir_loc}"
      exit ${DIR_INVALID}
   fi

   cd "${dir_loc}"
   dir_loc="$(pwd)"
   cd "${prev_dir}"

   #TODO add a space check.
   echo "${dir_loc}"
}

printline()
{
   echo "${1}"
   #Always put everything in the log
   if [ "x${logfile}" != "x" ]; then
       echo "${1}" >>${logfile}
   fi
}

check_exit()
{
   stat="${1}"
   if [ "${stat}" != "0" ] ; then
      printline "${2}"
      exit ${stat}
   fi
}

get_input()
{
   # get user input. Use readline to make things nice (-e)
   read -e -p "${1}: " INPUT dummy
   echo ${INPUT}
}

get_defaults()
{
    rm -f ${WORKFILE}
    if [ -f "${CIAOINSTALLRC}" ] ; then
        # Remember bash shells out loops so we need to store
        # the results when a match is found.
	cat "${CIAOINSTALLRC}" |
	while read var def dummy
	do
            if [ "x${var}" != "x" ] && \
		[ "x$(echo ${var} | grep ^#)" == "x" ] && \
		[ "x${def}" != "x" ] ; then
		if [ "${var}" == "${1}" ] ; then
                    echo ${def} > ${WORKFILE}
		fi
            fi
	done

	if [ -f ${WORKFILE} ] ; then
            RCRET=$(cat ${WORKFILE})
            rm -f ${WORKFILE}
	else
            RCRET="NONE"
	fi
    else
	RCRET="NONE"
    fi
    echo ${RCRET}
}

get_all_defaults()
{
    ret=$(get_defaults DL_DIR)
    if [ "x${ret}" != "x" ] && [ "x${ret}" != "xNONE" ] ; then
	DEF_DL_DIR="${ret}"
    fi

    # if $ASCDS_INSTALL is set use $ASCDS_INSTALL/.. 
    # instead of the default.

    if [ "x${ASCDS_INSTALL}" != "x" ] ; then
	DEF_INS_DIR="$(dirname ${ASCDS_INSTALL})"
    else
	ret=$(get_defaults INS_DIR)
	if [ "x${ret}" != "x" ] && [ "x${ret}" != "xNONE" ] ; then
	    DEF_INS_DIR="${ret}"
	fi
    fi
    ret=$(get_defaults RUN_SMOKE)
    if [ "x${ret}" != "x" ] && [ "x${ret}" != "xNONE" ] ; then
	DEF_RUN_SMOKE="${ret:0:1}"
    fi
    ret=$(get_defaults DELETE_TAR)
    if [ "x${ret}" != "x" ] && [ "x${ret}" != "xNONE" ] ; then
	DEF_DELETE_TAR="${ret:0:1}"
    fi
}

write_defaults()
{
    if [ "x${ciao_prefix}" == "x" ] ; then
	ret=$(get_defaults INS_DIR)
	if [ "x${ret}" != "x" ] && [ "x${ret}" != "xNONE" ] ; then
	    INS_DIR="${ret}"
	fi
    fi
    rm -f "${CIAOINSTALLRC}"
    echo "# This is a generated file. Do not edit." > "${CIAOINSTALLRC}"
    echo "# Download directory" >> "${CIAOINSTALLRC}"
    echo "DL_DIR ${ciao_cache}" >> "${CIAOINSTALLRC}"
    echo "# Install directory" >> "${CIAOINSTALLRC}"
    echo "INS_DIR ${ciao_prefix}" >> "${CIAOINSTALLRC}"
    echo "# Run Smoke tests upon completion?" >> "${CIAOINSTALLRC}"
    echo "RUN_SMOKE ${req_tests}" >> "${CIAOINSTALLRC}"
    echo "# Delete download tar files after update?" >> "${CIAOINSTALLRC}"
    echo "DELETE_TAR ${del_cache}" >> "${CIAOINSTALLRC}"
    sync
}

remove_element()
{
   #Used by --remove seg and add_unique
   element="$(echo "${1}" | tr '[:upper:]' '[:lower:]')"
   shift
   if [ "${element}" == "caldb_main" ] ; then
       element="caldb"
   fi
   list="${@}"
   list=$(echo "${list} " | sed "s/${element} //g")
   
   echo "${list}"
}

add_unique()
{
   #Used by --add seg and build_deps to prepend unique names
   element="$(echo "${1}" | tr '[:upper:]' '[:lower:]')"
   shift
   list="${@}"
   list=$(remove_element ${element} ${list})
   list=$(echo "${list} " | sed "s/${element} //g")
   list="${element} ${list}"
   list=$(echo "${list}" | sed "s/\ \ / /g")

   echo "${list}"
}

download_spec_files()
{
   sys="${1}"
   cd ${env_files_path}
   for seg in ${special_segs} ; do
      #Only ciao and sherpa are platform (${SYS}) dependent
      if [ "${seg}" == "ciao" ] || [ "${seg}" == "sherpa" ] || [ "${seg}" == "ciao-src" ] || [ "${seg}" == "sherpa-src" ] || [ "${seg}" == "marx" ] ; then
         download_files ${BASE_URL}/${SPEC_FILES_DIR}/${seg}-${sys}-spec.txt || exit ${DOWNLOAD_FAILED}
      else
         download_files ${BASE_URL}/${SPEC_FILES_DIR}/${seg}-spec.txt || exit ${DOWNLOAD_FAILED}
      fi
   done
}

download_files()
{
   FILE_URL="${1}"

   if [ "$#" == 2 ] ; then
      OUTFILE="-o ${2}"
   else
      OUTFILE="-O"
   fi

   ${DOWNLOAD_PROG} ${OUTFILE} ${FILE_URL}
   if [ "$?" != 0 ] ; then
      printline "ERROR: Failed to download the following:"
      printline "    ${FILE_URL}"
      exit ${DOWNLOAD_FAILED}
   fi
}

verify_conda()
{
   if [ ! -f "${1}/etc/profile.d/conda.sh" ] ; then
      printline "ERROR: Cannot find valid base conda install here - ${1}"
      if [ "x${install_only}" != "x" ] ; then
         printline "   invalid cache dir."
      else
         printline "   failed to install program."
      fi
      exit ${CONDA_INVALID}
   fi
}

verify_spec_files()
{
   if [ ! -d "${env_files_path}" ] || [ -z "$(ls ${SPEC_FILE})" ] ; then
      printline "ERROR: Cannot find ${SPEC_FILE}"
      if [ "x${install_only}" != "x" ] ; then
         printline "   invalid cache dir."
      else
         printline "   failed to populate the environment file cache"
      fi
      exit ${SPECS_INVALID}
   fi
}

check_caldb()
{
   caldb_from="${1}"
      
   #Check if dir and version file exists
   if [ ! -d "${caldb_from}" ] || [ ! -f "${caldb_from}/${CALDB_VER}" ] ; then
      printline "ERROR: Invalid CALDB directory to link in:"
      printline "   ${caldb_from}"
      exit ${CALDB_NOT_FOUND}
   fi
}

move_caldb()
{
   caldb_to="${1}"

   #If CALDB already installed, move it
   if [ -L "${caldb_to}/CALDB" ] ; then
      printline "Warning: Found CALDB symlink in ${caldb_to}"
      printline "   Renaming to CALDB.save"
      cd ${caldb_to}
      rm -rf CALDB.save
      mv -f CALDB CALDB.save
   fi
}

link_caldb()
{
   caldb_from="${1}" ; caldb_to="${2}"

   printline "Linking CALDB from: ${caldb_from}"
   printline "To: ${caldb_to}/CALDB"

   #Move CALDB if symlink present
   move_caldb ${caldb_to}
   if [ -d "${caldb_to}/CALDB" ] ; then
      printline "ERROR: Found CALDB directory already installed here:"
      printline "      ${caldb_to}/CALDB"
      printline "   Remove before trying again..."
      exit ${LINK_CALDB_FAIL}
   fi
   #Link in CALDB from external location
   cd ${caldb_to}
   ln -s "${caldb_from}" CALDB
}

build_deps()
{
   #Convert all segments to lowercase
   segments="$(echo "${segments}" | tr '[:upper:]' '[:lower:]')"

   #If "caldb_main" specified: switch to "caldb" (and check that it is not already specified)
   if [ "$(echo ${segments} | grep -e "caldb_main")" != "" ] ; then
      segments="$(remove_element caldb_main ${segments})"
      segments="$(add_unique caldb ${segments})"
   fi
   #If contains acis or hrc: Remove caldb (if exists and add to front)
   if [ "$(echo ${segments} | grep -e "hrc" -e "acis")" != "" ] ; then
      segments="$(add_unique caldb ${segments})"
   fi
   #If contains sherpa-src: Remove sherpa (if exists) and add to front
   if [ "$(echo ${segments} | grep -e "sherpa-src")" != "" ] ; then
      segments="$(add_unique ciao-src ${segments})"
      segments="$(add_unique sherpa ${segments})"
   fi
   #If contains sherpa, contrib, or caldb: Remove CIAO (if exists) and add to front
   if [ "$(echo ${segments} | grep -e "contrib" -e "sherpa" -e "src" -e "marx")" != "" ] ; then
      segments="$(add_unique ciao ${segments})"
   fi
}

download_or_install()
{
   SYS="${1}"
   #Source conda profile if not just installing caldb symlink
   if ! ([ "x${segments}" == "xcaldb " ] && [ "x${existing_caldb}" != "x" ]) ; then
      source ${2}/etc/profile.d/conda.sh
   fi

   if [ "x${3}" != "x" ] ; then
      export CONDA_PKGS_DIRS="${3}"
   elif [ "x${download_all}" != "x" ] ; then
      export CONDA_PKGS_DIRS="${ciao_cache}/pkgs-${sys}"
   fi

   #-y: auto-yes. download/install flags. --copy: no hardlinks
   conda_opts="-y ${download_only} ${install_only} --copy --file "

   if [ "x${ciao_path}" != "x" ] ; then
      conda_opts="-p ${ciao_path} ${conda_opts}"
   else
      conda_opts="-n fake ${conda_opts}"
   fi

   rm -f ${env_files_path}/final.txt
   touch ${env_files_path}/final.txt

   for seg in ${segments} DUMMY ; do
       case ${seg}
          in
          ciao|sherpa|ciao-src|sherpa-src|marx )
             cat ${env_files_path}/${seg}-${SYS}-spec.txt >> ${env_files_path}/final.txt
             ;;
          caldb )
             #Move CALDB symlink if it already exists
             move_caldb ${ciao_path}
             if [ "x${existing_caldb}" == "x" ] ; then
                #Add caldb to install list (unless symlinking)
                cat ${env_files_path}/${seg}-spec.txt >> ${env_files_path}/final.txt
             fi
             ;;
          contrib )
             cat ${env_files_path}/${seg}-spec.txt >> ${env_files_path}/final.txt
             ;;
          acis|hrc )
             if [ "${existing_caldb}x" != "x" ] || [ -L "${caldb_to}/CALDB" ] ; then
                printline "WARNING: ${seg} requested, but caldb is symlinked. Skipping..."
             else
                #If caldb is linked, don't try to update this...
                cat ${env_files_path}/${seg}-spec.txt >> ${env_files_path}/final.txt
             fi
             ;;
          DUMMY ) : ;;
          * )
             printline "ERROR: Unknown segment - ${seg}"
             usage
             return ${UNKNOWN_ARGUMENT}
             ;;
       esac
   done
   printline "Installing segments..."

   if [ "x${segments}" == "xcaldb " ] && [ "x${existing_caldb}" != "x" ] ; then
      mkdir -p ${ciao_path}
   else
      eval ${CONDA_PROG} ${ins_type} ${conda_opts} ${env_files_path}/final.txt ${output_direct} || return ${INSTALL_FAILED}
   fi
   #Symlink caldb if requested and --download-only not specified
   if [ "x${existing_caldb}" != "x" ] && [ "x${download_only}" == "x" ] ; then
      link_caldb ${existing_caldb} ${ciao_path}
   fi
   #Switch from "create" to "install" if not done so already
   if [ -d ${ciao_path} ] && [ "x${download_only}" == "x" ] ; then ins_type="install" ; fi
}

umask 022
#set -e -x
while [ "$#" -ne 0 ] ; do
   case "${1}" in
      --prefix|-p )
         if [ "x${2}" == "x" ] ; then
            printline "ERROR: Argument expected for ${1}"
            exit ${DIR_NOT_SUPPLIED}
         fi
         ciao_prefix="${2}"
         shift
         ;;
      --cache )
         if [ "x${2}" == "x" ] ; then
            printline "ERROR: Argument expected for ${1}"
            exit ${DIR_NOT_SUPPLIED}
         fi
         ciao_cache="${2}"
         shift
         ;;
      --pkg-cache )
         if [ "x${2}" == "x" ] ; then
            printline "ERROR: Argument expected for ${1}"
            exit ${DIR_NOT_SUPPLIED}
         fi
         pkg_cache="${2}"
         shift
         ;;
      --system )
         if [ "x${2}" != "x" ] ; then
            worksys=$(echo "${2}" | tr '[:upper:]' '[:lower:]')
            case "${worksys}" in
               linux )
                  SYS="Linux" ;;
               macos | osx )
                  SYS="macOS" ;;
               macos-arm )
                  SYS="macOS-ARM" ;;
               * )
                  printline "ERROR: Unknown system ${2}"
                  exit ${UNKNOWN_ARGUMENT} ;;
            esac
            shift
         else
            printline "ERROR: Argument expected for ${1}"
            exit ${UNKNOWN_ARGUMENT}
         fi
         ;;
    --add )
      if [ "x${2}" == "x" ] ; then
         printline "ERROR: Argument expected for ${1}"
         exit ${UNKNOWN_ARGUMENT}
      fi
      segments="$(add_unique ${2} ${segments})"
      shift
      ;;
    --remove )
      if [ "x${2}" == "x" ] ; then
         printline "ERROR: Argument expected for ${1}"
         exit ${UNKNOWN_ARGUMENT}
      fi
      segments="$(remove_element ${2} ${segments})"
      shift
      ;;
    --delete-cache )
      del_cache="y"
      ;;
    --download-only )
      download_only="--download-only"
      del_cache="n"
      ;;
    --download-all )
      download_only="--download-only"
      del_cache="n"
      download_all="y"
      segments="ciao sherpa marx contrib caldb acis hrc"
      if [ "x${2}" == "x" ] ; then
         printline "ERROR: Argument expected for ${1}"
         exit ${UNKNOWN_ARGUMENT}
      fi
      ciao_cache="${2}"
      shift
      ;;
    --logdir )
      if [ "x${2}" == "x" ] ; then
         printline "ERROR: Argument expected for ${1}"
         exit ${DIR_NOT_SUPPLIED}
      fi
      logdir="${2}"
      shift
      ;;
    --install-only )
      install_only="--offline"
      ;;
    --with-top )
      if [ "x${2}" == "x" ] ; then
         printline "ERROR: Argument expected for ${1}"
         exit ${UNKNOWN_ARGUMENT}
      fi
      fake_top="${2}"
      shift
      ;;
    --link-caldb )
      if [ "x${2}" == "x" ] ; then
         printline "ERROR: Argument expected for ${1}"
         exit ${UNKNOWN_ARGUMENT}
      fi
      existing_caldb="${2}"
      segments="$(add_unique caldb ${segments})"
      shift
      ;;
    --skip-tests )
      req_tests="n"
      ;;
    --small-files )
      #Constructors speed up installs on slow connections, but hinder
      # installs where disconnects are common
      if [ "x${ins_type}" == "xconstruct" ] ; then
         ins_type="create"
      fi
      ;;
    --force )
      force="y"
      ;;
    --update )
      ins_type="install"
      ;;
    -v|--verbose )
      export verbose="y"
      ;;
    --help|-h )
      usage
      exit ${OK}
      ;;
    * )
      printline "ERROR: Unknown option ${1}."
      exit ${UNKNOWN_ARGUMENT}
      ;;
  esac
  shift
done

#-------------------------------------------------------------------------
#                        Get & Validate Input
#-------------------------------------------------------------------------

get_all_defaults

#Check if a log dir was supplied. Get the full path
logdir=$(valid_dir ${logdir})
check_exit ${?} "${logdir}"
logfile="${logdir}/${LOGFILE_NAME}"
touch ${logdir}/${LOGFILE_NAME}
if [ "${?}" != "0" ] ; then
    exit ${DIR_INVALID}
fi
if [ "${verbose}" == "y" ] ; then
   export output_direct="2>&1 | tee -a ${logfile}"
else
   export output_direct="1>>${logfile} 2>&1"
fi
echo "Script Log file is ${logfile}"
echo ""

if [ "x${download_only}" != "x" ] && [ "x${install_only}" != "x" ] ; then
    printline "ERROR: Cannot use --download-only and --install-only together."
    exit ${UNKNOWN_ARGUMENT}
fi

if [ "x${segments}" == "x" ] ; then
   printline "ERROR: No segments selected to download/install."
   exit ${INVALID_SPECS}
fi

if [ "x${ciao_cache}" == "x" ] ; then  
   if [ "x${install_only}" != "x--offline" ] ; then
      ciao_cache=$(get_input "Download directory for tar files (${DEF_DL_DIR})")
   else
      ciao_cache=$(get_input "Location of downloaded tar files (${DEF_DL_DIR})")
   fi
  
   #If no response, use default 
   if [ "${ciao_cache}x" == "x" ] ; then
      ciao_cache="${DEF_DL_DIR}"
   fi
fi

#Check if a valid cache prefix was supplied. Get the full path
ciao_cache=$(valid_dir ${ciao_cache})
check_exit ${?} "${ciao_cache}"

#Check if a valid package cache was supplied. Get the full path
if [ "x${pkg_cache}" != "x" ] ; then
   pkg_cache=$(valid_dir ${pkg_cache} nowrite)
   check_exit ${?} "${pkg_cache}"
fi

if [ "${existing_caldb}x" != "x" ] ; then
   check_caldb ${existing_caldb}
   existing_caldb=$(valid_dir ${existing_caldb} nowrite)
   check_exit ${?} "${existing_caldb}"
fi

#Set and create install dirs
export env_files_path="${ciao_cache}/env_files"
mkdir -p ${ciao_cache}

#If install requested (NOT download only)
if [ "x${download_only}" == "x" ] ; then 
   if [ "x${ciao_prefix}" == "x" ] ; then
      #Prompt for input if no prefix given
      ciao_prefix=$(get_input "CIAO installation directory (${DEF_INS_DIR})")

      #If no response, use default
      if [ "${ciao_prefix}x" == "x" ] ; then
         ciao_prefix="${DEF_INS_DIR}"
      fi
   fi

   #Check if a valid install prefix was supplied. Get the full path
   ciao_prefix="$(valid_dir ${ciao_prefix})"
   check_exit ${?} "${ciao_prefix}"
   
   export ciao_path="${ciao_prefix}/ciao-${CIAO_VER}"
   export scripts_path="${ciao_prefix}/ciao-${CIAO_VER}/init"
   mkdir -p ${ciao_prefix}

   #Update if existing?
   if [ -d ${ciao_path} ] && ( [ "x${force}" != "xy" ] || [ "x${ins_type}" == "update" ] ); then
      update=$(get_input "${ciao_path} already exists. Update? (y|n) (y)")
      update="$(echo "${update}" | tr '[:upper:]' '[:lower:]')"
      if [ "${update}" == "n" ] ; then
         printline "Install already exists. Exiting..."
         printline "Use --force to overwrite"
         exit ${INSTALL_FAILED}
      fi
      update="y"
      ins_type="install"
   fi
   
   #Cleanup cache?
   until [ "x${del_cache}" == "xy" ] || [ "x${del_cache}" == "xn" ] ; do
      del_cache=$(get_input "Delete the cache after install? (y|n) (${DEF_DELETE_TAR:0:1})")
      if [ "${del_cache}x" == "x" ] ; then
         #Default
         del_cache="${DEF_DELETE_TAR:0:1}"
      fi
      del_cache="$(echo "${del_cache}" | tr '[:upper:]' '[:lower:]')"
   done

   #If did not specify smoke test skip, check?
   until [ "x${req_tests}" == "xy" ] || [ "x${req_tests}" == "xn" ] ; do
      req_tests=$(get_input "Run smoke tests? (y|n) (${DEF_RUN_SMOKE:0:1})")
      if [ "${req_tests}x" == "x" ] ; then
         #Default
         req_tests="${DEF_RUN_SMOKE:0:1}"
      fi
      req_tests="$(echo "${req_tests}" | tr '[:upper:]' '[:lower:]')"
   done
fi

#Update defaults if requested
ans=$(get_input "Save these settings? (y|n) (y)")
ans="$(echo "${ans}" | tr '[:upper:]' '[:lower:]')"
if [ "x${ans}" == "x" ] || [ "x${ans:0:1}" == "xy" ] ; then
   write_defaults
fi

#-------------------------------------------------------------------------
#                        Check System
#-------------------------------------------------------------------------

#Check for a proper download tool
if [ "x$(which curl)" != "x" ] ; then
  export DOWNLOAD_PROG="curl -Ls"
else
  printline "ERROR: Could not find curl for downloading."  
  exit ${INSTALL_FAILED}
fi

#Check the real system
if [ "$(uname)" == "Darwin" ] && [ "$(uname -m)" == "arm64" ] ; then
   OS_TYPE="osx-arm64"
   RSYS="macOS-ARM"
   #Conda constructors are not supported with ciao-install-ng for macOS ARM
   #Waiting on https://github.com/conda-forge/conda-standalone-feedstock/pull/54
   ins_type="create"
elif [ "$(uname)" == "Darwin" ] ; then
   OS_TYPE="osx-64"
   RSYS="macOS"
else
   OS_TYPE="linux-64"
   RSYS="Linux"
fi

#Allow override for local spec file and tar (--system)
if [ "${SYS}x" == "x" ]; then
   SYS="${RSYS}"
else
   if [ "${RSYS}" != "${SYS}" ] ; then
      req_tests="n"
      case "${RSYS}" in
         macOS-ARM )
            if [ "${SYS}" == "Linux" ] || [ "${SYS}" == "macOS" ] ; then
               printline "WARNING: Installing ${SYS} on ${RSYS}"
               printline "         Will skip ahelp indexing and tests."
               cross_platform="y"
            fi ;;
         macOS )
            if [ "${SYS}" == "Linux" ] || [ "${SYS}" == "macOS-ARM" ] ; then
               printline "WARNING: Installing ${SYS} on ${RSYS}"
               printline "         Will skip ahelp indexing and tests."
               cross_platform="y"
            fi ;;
         Linux )
            if [ "${SYS}" == "macOS" ] || [ "${SYS}" == "macOS-ARM" ] ; then
               printline "WARNING: Installing ${SYS} on ${RSYS}"
               printline "         Will skip ahelp indexing and tests."
               cross_platform="y"
            fi ;;
      esac
   fi
fi
export SYS RSYS


#-------------------------------------------------------------------------
#                        Prepare to Download/Install
#-------------------------------------------------------------------------

build_deps

if [ "${ins_type}" == "construct" ] ; then
   #Cannot use conda constructor cross-platform
   #Cannot use conda constructor for download only if installing more that CIAO+Sherpa
   # as the cache would automatically try to populate for all of the segments anyway
   # (skipping the constructor minimizes downtime in this case)
   if [ "${cross_platform}" == "y" ] || \
      ( [ "${segments}" != "ciao sherpa " ] && [ "x${download_only}${install_only}" != "x" ] ) || \
      [ "$(echo ${segments} | grep -e "sherpa")" == "" ] ; then
      ins_type="create"
   fi
fi

#Conda specification file
export SPEC_FILE="${env_files_path}/*-spec.txt"
export conda_dir_path="${ciao_cache}/conda_loc-${OS_TYPE}"
mkdir -p ${ciao_cache}/${CONSTRUCTORS_DIR} ${ciao_cache}/${CONDA_INSTALLERS_DIR} ${env_files_path}
conda_installer_url="${BASE_URL}/${CONDA_INSTALLERS_DIR}/conda-${OS_TYPE}"
conda_installer_path="${ciao_cache}/${CONDA_INSTALLERS_DIR}/conda-${OS_TYPE}.sh"

#Check for segments beyond ciao and sherpa for conda constructor
# These are to be installed via spec file
special_segs="${segments}"
if [ "${ins_type}" == "construct" ] ; then
   special_segs="$(remove_element sherpa ${special_segs})"
   special_segs="$(remove_element ciao ${special_segs})"
fi

#If there are segments to install via spec file
#And if the only additional segment to install is not a CALDB symlink
if [ "x${special_segs}" != "x" ] && ! ( [ "x${special_segs}" == "xcaldb " ] && [ "x${existing_caldb}" != "x" ] ) ; then
   #Download the latest spec files
   if [ "${install_only}x" == "x" ] ; then

      #Download conda if not already installed
      if [ ! -f ${conda_dir_path}/etc/profile.d/conda.sh ] ; then
         cd ${ciao_cache}/${CONDA_INSTALLERS_DIR}
         echo "Downloading Conda..."
         download_files ${conda_installer_url} ${conda_installer_path}
         check_exit ${?}
      fi
   fi

   #Check for or install conda
   if [ ! -f ${conda_dir_path}/etc/profile.d/conda.sh ] ; then
      #If conda exists, but does not have the conda.sh file = invalid
      if [ -d ${conda_dir_path} ] ; then
         printline "ERROR: Invalid base Conda install here - ${conda_dir_path}"
         exit ${CONDA_INVALID} 
      fi

      #If conda is not installed and the installer was not downloaded -> error
      if [ ! -f ${conda_installer_path} ] ; then
         printline "ERROR: ${ciao_cache}/conda-${OS_TYPE}.sh not found."
         exit ${CONDA_INVALID}
      fi
      
      #Otherwise, install conda
      eval bash ${conda_installer_path} -b -p ${conda_dir_path} ${output_direct}
      if [ "$?" != 0 ] ; then
         printline "ERROR: Failed to install Conda here - ${conda_dir_path}"
         exit ${CONDA_INVALID}
      fi
   fi
   echo "Verifying Conda..."
   verify_conda ${conda_dir_path} || exit ${?}
fi

#-------------------------------------------------------------------------
#                          Download/Install
#-------------------------------------------------------------------------

#Install segments OR populate the package cache
if [ "x${download_only}" != "x" ] ; then
   printline "Populating the cache for: ${segments}"
else
   printline "Installing: ${segments}"
fi

if [ "x${download_all}" == "xy" ] ; then
   systems="Linux macOS macOS-ARM"
else
   systems="${SYS}"
fi

for sys in ${systems} ; do
   printline "For ${sys}"
   #Download and/or install conda constructor
   if [ "${ins_type}" == "construct" ] ; then
      #Download constructor unless install_only was set
      constructor_path="${ciao_cache}/${CONSTRUCTORS_DIR}/ciao-${INSTALLER_VER}-${RSYS}.sh"
      if [ "x${install_only}" != "x--offline" ] ; then
         constructor_url="${BASE_URL}/${CONSTRUCTORS_DIR}/ciao-${INSTALLER_VER}-${RSYS}"
         download_files ${constructor_url} ${constructor_path} || exit ${?}
      fi
      #Install constructor unless download_only was set
      if [ "x${download_only}" != "x--download-only" ] ; then
         if [ ! -f ${constructor_path} ] ; then
            printline "ERROR: Could not find constructor here - "
            printline "   ${constructor_path}"
            if [ "x${install_only}" == "x--offline" ] ; then
               printline " Try again without --install-only."
            else
               printline " Failed to download."
            fi
            exit ${INSTALL_FAILED}
         fi
         eval bash ${constructor_path} -b -p ${ciao_path} ${output_direct}
         if [ "$?" != 0 ] ; then
            printline "ERROR: Failed to install CIAO here - ${ciao_path}"
            exit ${INSTALL_FAILED}
         fi
      fi
      ins_type="install"
   fi
   #Download and/or install additional segments from spec file
   if [ "${special_segs}x" != "x" ] ; then
      segments="${special_segs}"
      if [ "x${install_only}" == "x" ] ; then
         echo "Downloading environment files for ${sys}..."
         download_spec_files ${sys}
         check_exit ${?} "Failed to download environment files for ${sys}"
      fi
      #Populate cache and/or install
      download_or_install ${sys} ${conda_dir_path} ${pkg_cache}
      if [ "${?}" != "0" ] ; then
         printline "ERROR: Failed to download/install CIAO packages here - ${ciao_path}."
         exit ${INSTALL_FAILED}
      fi
   fi
done

if [ "x${download_only}" != "x--download-only" ] ; then
   #Check for fixes to apply (will only exist if CIAO is present)
   if [ -f ${ciao_path}/bin/ciao-configure ] ; then
      printline "Fixing install..."
      ${ciao_path}/bin/ciao-configure ${output_redirect}
   fi


   #-------------------------------------------------------------------------
   #                     Ahelp indexes & Smoke tests
   #-------------------------------------------------------------------------

   #If cross-platform or no CIAO: skip ahelp indexing and smoke tests
   if [ "${cross_platform}" != "y" ] && [ -f "${ciao_path}/bin/ahelp" ] ; then
      source ${ciao_path}/bin/ciao.sh
      #Re-create the ahelp index
      printline "Re-indexing ahelp system"
      ahelp -r 1>/dev/null
   
      if [ "${req_tests}" == "y" ] ; then
         cd ${ASCDS_INSTALL}/test
         #Always output test progress
         printline "Running smoke tests"
         bash smoke/bin/run_smoke_tests.sh 2>&1 | tee -a ${logfile}
      fi
   else
      printline "Installing cross-platform or CIAO not installed"
      printline "  Skipping ahelp reindexing and smoke tests..."
   fi

   if [ "${fake_top}x" != "x" ] && [ -f ${ciao_path}/bin/ciao-configure ] ; then
       printline "--with-top supplied - Fixing top to final location..."
       ${ciao_path}/bin/ciao-configure ${fake_top} ${output_redirect}
   fi
fi

#Delete the cache contents unless otherwise requested
if [ "${del_cache}" == "y" ] ; then
   if [ ! -z ${env_files_path} ] && [ -d ${env_files_path} ] ; then
      #Cleanup environment/specification files
      rm -rf ${env_files_path}/*.txt
      stat="${?}"
      if [ "${stat}" != "0" ] ; then
         printline "WARNING: Could not remove some environment files here:"
         printline "   ${env_files_path}"
         printline "  Check permissions and remove this directory."
      elif [ -z "$(ls -A ${env_files_path})" ] ; then
         #Remove ${env_files_path} if empty
         rm -rf ${env_files_path}
      else
         printline "WARNING: The environment file cache directory is not empty:"
         printline "   ${env_files_path}"
         printline "  Skipping removal of the directory above."
      fi
   fi
   #Cleanup constructors
   if [ -d ${ciao_cache}/${CONSTRUCTORS_DIR} ] ; then
      #If the constructor file was not downloaded, then don't try to delete
      if [ ! -z ${constructor_path} ] && [ -f ${constructor_path} ] ; then
         #If it exists, only try to remove if writeable
         if [ -w ${constructor_path} ] ; then
            rm -rf ${constructor_path}
            stat="${?}"
            if [ "${stat}" != "0" ] ; then
               printline "WARNING: Failed to cleanup constructor file from the cache:"
               printline "   ${constructor_path}"
            fi
         else
            printline "WARNING: Skipping cleanup of this file:"
            printline "   ${constructor_path}"
            printline "  No write permissions."
         fi
      fi
      #If the constructor dir is now empty, remove it
      if [ -z "$(ls -A ${ciao_cache}/${CONSTRUCTORS_DIR})" ] ; then
         #Remove ${ciao_cache}/${CONSTRUCTORS_DIR} if empty
         rm -rf ${ciao_cache}/${CONSTRUCTORS_DIR}
      else
         printline "WARNING: The following constructor cache directory is not empty:"
         printline "   ${ciao_cache}/${CONSTRUCTORS_DIR}"
         printline "  Skipping removal of the directory above."
      fi
   fi

   #Cleanup conda installers
   if [ ! -z ${ciao_cache} ] && [ -d ${ciao_cache}/${CONDA_INSTALLERS_DIR} ] && \
         [ -f ${conda_installer_path} ] ; then
      #conda_installer_path
      if [ -w ${conda_installer_path} ] ; then
         rm -f ${conda_installer_path}
         stat="${?}"
         if [ "${stat}" != "0" ] ; then
            printline "WARNING: Failed to cleanup this conda installer from the cache:"
            printline "   ${conda_installer_path}"
         fi
         if [ -z "$(ls -A ${ciao_cache}/${CONDA_INSTALLERS_DIR})" ] ; then
            #Remove ${ciao_cache}/${CONDA_INSTALLERS_DIR} if empty
            rm -rf ${ciao_cache}/${CONDA_INSTALLERS_DIR}
         fi
      else
         printline "WARNING: Skipping cleanup of this conda installer from the cache:"
         printline "   ${conda_installer_path}"
         printline "  No write permissions."
      fi
   fi
   #Cleanup conda installation
   if [ ! -z ${conda_dir_path} ] && [ -d ${conda_dir_path} ] ; then
      if [ -w ${conda_dir_path} ] ; then
         rm -rf ${conda_dir_path}
         stat="${?}"
         if [ "${stat}" != "0" ] ; then
            printline "WARNING: Failed to cleanup this conda directory"
            printline "   ${conda_dir_path}"
         fi
      else
         printline "WARNING: Skipping cleanup of this conda directory"
         printline "   ${conda_dir_path}"
         printline "  No write permissions."
      fi
   fi
fi


if [ "x${download_only}" == "x" ] ; then
   #If install performed
   if [ -f "${ciao_path}/bin/ciao.sh" ] ; then
      #If CIAO is installed
      printline "To use the install:"
      printline "	(t)csh: $> source ${ciao_path}/bin/ciao.csh"
      printline "	bash/zsh: $> source ${ciao_path}/bin/ciao.sh"
   elif [ -d "${ciao_path}/CALDB" ] ; then
      #If just CALDB is installed without CIAO
      printline "CALDB is installed here:"
      printline "   ${ciao_path}/CALDB"
   fi
else
   #If --download-only selected
   printline "Cache populated here: ${ciao_cache}"
fi
