#!/usr/bin/env bash
#------------------------------------------------------------------------------#
# This file is part of PALM.
#
# PALM 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 3 of the License, or (at your option) any later 
# version.
#
# PALM 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
# PALM. If not, see <http://www.gnu.org/licenses/>.
#
# Copyright 1997-2018  Leibniz Universitaet Hannover
#------------------------------------------------------------------------------#
# palm_installer - an automatic installation scipt for PALM
#------------------------------------------------------------------------------#

# script version
installer_version="v0.6.2"

# log all output of this script in a file
this_script=$(basename $0)
logfile_all="$(pwd)/palm_installer.log"
rm -f ${logfile_all}
exec >  >(tee -ia ${logfile_all})
exec 2> >(tee -ia ${logfile_all} >&2)

# opens a terminal in case started with a double click
if [ ! -t 0 ]; then
   if type konsole > /dev/null; then
      konsole --noclose -e $@
   elif type terminal > /dev/null; then
      xterm -hold -e $@
   fi
   exit 0
fi

get_number_of_cpu_cores() {
   {
      # OSX
      n=$(sysctl -n machdep.cpu.core_count 2> /dev/null)
   } || {
      # Linux
      n=$(grep -c ^processor /proc/cpuinfo 2> /dev/null)
   } || {
      # fallback
      if ! [[ $n =~ ^-?[0-9]+$ ]]; then
         n=2
      fi
   }
   echo $n
}

install_prefix=${HOME}/palm/current_version
shell_profile=${HOME}/.bashrc
program_name="PALM"
NUM_PROC_BUILD=$(get_number_of_cpu_cores)
max_cores_test=4
NUM_PROC_TEST=$(($NUM_PROC_BUILD<${max_cores_test}?$NUM_PROC_BUILD:${max_cores_test}))
palm_base_url="https://palm.muk.uni-hannover.de"
palm_svn_url="${palm_base_url}/svn/palm/trunk"
palm_min_rev=2774
number_of_cols=70
logfile_test="palm_test.log"

palm_installer_url="http://palm.muk.uni-hannover.de/trac/raw-attachment/wiki/doc/install/automatic/palm_installer"
newest_installer_version=$(wget -qO- ${palm_installer_url} | \
                           sed -n '/installer_version=\"v.*/p' | \
                           sed  's/installer_version=\"\(v.*\)\"/\1/g')

palm_installer_update() {
   hrule
   if [[ "${newest_installer_version}" == "" ]]; then
      printf "| \e[1;33m%-${number_of_cols}s\e[0m |\n" "Please make sure you are using the latest ${program_name} installer version."
   elif [[ "${newest_installer_version}" == "${installer_version}" ]]; then
      printf "| \e[1;32m%-${number_of_cols}s\e[0m |\n" "This automatic installer is up to date."
   else
      printf "\e[1;33m%-${number_of_cols}s\e[0m\n" "This automatic installer is outdated!"
      palm_read_yn "Do you wish to update to ${newest_installer_version}?" "RESPONSE_INSTALLER_UPDATE"
      if [[ "$RESPONSE_INSTALLER_UPDATE" == "y" ]]; then
         if wget ${palm_installer_url} --output-document=${this_script} &> /dev/null ; then
            printf "\e[1;32m%-${number_of_cols}s\e[0m\n" "Successfully updated to ${newest_installer_version}"
            bash ${this_script}
            exit 0
         else
            printf "\e[1;33m%-${number_of_cols}s\e[0m\n"  "Update failed. Please manually download the newest installer from."
            printf "\e[1;33m%-${number_of_cols}s\e[0m\n"  "   https://palm.muk.uni-hannover.de/trac/wiki/doc/install#automatic"
            palm_installer_abort_message
         fi
      else
         printf "\e[1;33m%-${number_of_cols}s\e[0m\n"  "WARNING: You are using an outdated ${program_name} installer."
         printf "\e[1;33m%-${number_of_cols}s\e[0m\n"  "         You will not get any support in case something goes wrong."
      fi
   fi
}

palm_installer_check_software_all() {
   hrule
   printf "%s\n" "Checking general software requirements..."
   palm_installer_check_software_required "a Fortran compiler" ifort ftn gfortran mpif90
   palm_installer_check_software_required "mpirun" mpirun
   palm_installer_check_software_optional "svn" "${program_name} has to be downloaded manually" svn
   palm_installer_check_software_required "make" make
   palm_installer_check_software_required "cmake" cmake
   palm_installer_check_software_optional "python3" "The GUI will not be available" python3
   if [[ "${required_software_available}" == "false" ]]; then
      hrule
      printf "| \e[1;31m%-${number_of_cols}s\e[0m |\n" "Please provide missing software and try again!"
      palm_installer_abort_message
   fi
   if [[ "${optional_software_available}" == "false" ]]; then
      printf "\e[1;33m%s\e[0m\n" "Some optional software is missing."
      palm_read_yn "Do you wish to continue anyways?" "RESPONSE_CONTINUE"
      hrule
      if [[ "$RESPONSE_CONTINUE" == "y" ]]; then
         printf "| \e[1;33m%-${number_of_cols}s\e[0m |\n"  "Some optional software is missing. Continue anyways..."
      else
         printf "| \e[1;31m%-${number_of_cols}s\e[0m |\n" "Please provide missing software and try again!"
         palm_installer_abort_message
      fi
   else
      hrule
      printf "| \e[1;32m%-${number_of_cols}s\e[0m |\n"  "All required software found. Lets continue..."
   fi
}

palm_installer_check_software() {
   local software_found="false"
   for current_software in ${@}; do
      if command -v ${current_software} > /dev/null; then
         local software_found="true"
         local found_software="${found_software} ${current_software}"
      fi
   done
   if [[ "${software_found}" == "false" ]]; then
      return 1
   else
      echo "${found_software}"
      return 0
   fi
   
}

palm_installer_check_software_required() {
   local description=${1}
   shift
   printf "%s %-25s " "-- Looking for" "${description}..."
   if palm_installer_check_software ${@} > /dev/null; then
      printf "\e[1;32mfound\e[0m$(palm_installer_check_software ${@})"
   else
      printf "\e[1;31mnot found!\e[0m"
      required_software_available="false"
   fi
   printf "\n"
}

palm_installer_check_software_optional() {
   local description=${1}
   shift
   local feature=${1}
   shift
   printf "%s %-25s " "-- Looking for" "${description} (optional)..."
   if palm_installer_check_software ${@} > /dev/null; then
      printf "\e[1;32mfound\e[0m$(palm_installer_check_software ${@})"
   else
      printf "\e[1;33mnot found!\e[0m (${feature})"
      optional_software_available="false"
   fi
   printf "\n"
}

# clean any old build and promt the user for all required info for installation
palm_installer_init() {
   hrule
   printf "\n"
   printf "The default installation directory is \"${install_prefix}\".\n"
   palm_read_yn "Do you wish to install ${program_name} in the default directory?" "RESPONSE_PREFIX"
   if [[ "$RESPONSE_PREFIX" != "y" ]]; then
      read -p "-- Please enter a new installation directory: " CUSTOM_PREFIX
      local input_dir=$(eval echo $CUSTOM_PREFIX)
      local tmp=""
      while ! readlink -f $input_dir > /dev/null; do
         tmp="/$(basename $input_dir)${tmp}"
         input_dir=$(dirname $input_dir)
      done
      install_prefix="$(readlink -f $input_dir)${tmp}"
   fi
   trunk_dir=${install_prefix}/trunk
   source_dir=${install_prefix}/.installer.tmp
   build_dir=${source_dir}/build
   printf "%s \"\e[1;32m${install_prefix}\e[0m\"\n" "-- ${program_name} will be installed in:"
   printf "\n"
   
   AVAILABLE_FORTRAN_COMPILER=$(palm_installer_check_software ftn mpif90 ifort gfortran)
   CUSTOM_FORTRAN_COMPILER=$(echo ${AVAILABLE_FORTRAN_COMPILER} | awk '{print $1;}')
   printf "%s\n" "Available Fortran compilers on this computer:${AVAILABLE_FORTRAN_COMPILER}"
   printf "%s \"\e[1;32m${CUSTOM_FORTRAN_COMPILER}\e[0m\".\n" "-- The default Fortran compiler for this installation is"
   palm_read_yn "Do you wish to use the default Fortran compiler?" "RESPONSE_COMPILER"
   if [[ "$RESPONSE_COMPILER" != "y" ]]; then
      read -p "-- Please enter your prefered Fortran compiler: " CUSTOM_FORTRAN_COMPILER
      while ! command -v ${CUSTOM_FORTRAN_COMPILER} > /dev/null; do
         printf "%s \e[1;31mnot found!\e[0m\n" "-- \"${CUSTOM_FORTRAN_COMPILER}\""
         read -p "Please try again: " CUSTOM_FORTRAN_COMPILER
      done
   fi
   printf "%s \"\e[1;32m${CUSTOM_FORTRAN_COMPILER}\e[0m\" will be used.\n" "-- The Fortran compiler"
   printf "\n"

   printf "%s\n" "This installer can try to find your NetCDF libraries automatically."
   palm_read_yn "Do you wish to use the automatic NetCDF library detection?" "RESPONSE_NETCDF"
   if [[ "$RESPONSE_NETCDF" != "y" ]]; then
      read -p "-- Please enter the netCDF C root path: " NETCDF_C_ROOT
      read -p "-- Please enter the netCDF Fortran root path: " NETCDF_FORTRAN_ROOT
      printf "%s \e[1;33mdisabled\e[0m.\n" "-- Automatic NetCDF library detection is"
   else
      printf "%s \e[1;32menabled\e[0m.\n" "-- Automatic NetCDF library detection is"
   fi
   printf "\n"

   if [[ "$palm_installer_build_rrtmg" == "true" ]]; then
      RRTMG_ROOT=${install_prefix}/rrtmg
      printf "%s\n" "This installer can install the RRTMG library for you."
      palm_read_yn "Do you wish to install the RRTMG library?" "RESPONSE_RRTMG"
      if [[ "$RESPONSE_RRTMG" == "y" ]]; then
         printf "%s\n" "-- The default installation directory is \"${RRTMG_ROOT}\"."
         palm_read_yn "Do you wish to install the RRTMG library in the default directory?" "RESPONSE_PREFIX"
         if [[ "$RESPONSE_PREFIX" != "y" ]]; then
            read -p "-- Please enter a new installation directory: " CUSTOM_PREFIX
            local input_dir=$(eval echo $CUSTOM_PREFIX)
            local tmp=""
            while ! readlink -f $input_dir > /dev/null; do
               tmp="/$(basename $input_dir)${tmp}"
               input_dir=$(dirname $input_dir)
            done
            RRTMG_ROOT="$(readlink -f $input_dir)${tmp}"
         fi
         printf "%s \"\e[1;32m${RRTMG_ROOT}\e[0m\"\n" "-- The RRTMG library will be installed in:"
      else
         palm_installer_build_rrtmg="false"
      fi
      printf "\n"
   fi

   if [[ -d ${trunk_dir} ]] && [[ "$(svn status ${trunk_dir})" == ""  ]]; then
      local existing_revision=$(palm_svn_local_revision)
   fi

   if [[ "${existing_revision}" != ""  ]]; then
      printf "%s\n" "A working copy of ${program_name} (revision: ${existing_revision}) was found..."
      palm_read_yn "Do you wish to update ${program_name}?" "RESPONSE_SVN"
   else
      printf "%s\n" "No working copy of ${program_name} was found so far..."
      palm_read_yn "Do you wish to download ${program_name} via Subversion?" "RESPONSE_SVN"
   fi
   
   if [[ "$RESPONSE_SVN" == "y" ]]; then
      SVN_USER=$(palm_svn_get_username)
      printf "%s \e[1;32m$SVN_USER\e[0m \n" "-- Your ${program_name} username is:"
      palm_svn_check_remote
      newest_revision=$(palm_svn_newest_revision)
      printf "%s\n" "-- By default the latest ${program_name} revision (revision: ${newest_revision}) will be aquired."
      palm_read_yn "Do you wish to get the latest ${program_name} revision?" "RESPONSE_REVISION"
      if [[ "$RESPONSE_REVISION" != "y" ]]; then
         read -p "-- Please enter the desired ${program_name} revision (${palm_min_rev} to ${newest_revision}): " CUSTOM_REVISION
         while [[ ${CUSTOM_REVISION} -gt ${newest_revision} ]] || [[ ${CUSTOM_REVISION} -lt ${palm_min_rev} ]]; do
            printf "%s \e[1;31mnot a valid ${program_name} revision!\e[0m \n" "--"
            printf "%s \e[1;31mThis installer only supports ${program_name} revisions >${palm_min_rev}!\e[0m \n" "--"
            sleep 0.2
            read -p "-- Please choose a value between ${palm_min_rev} and ${newest_revision}: " CUSTOM_REVISION
         done
      else
         CUSTOM_REVISION=${newest_revision}
      fi
      if [[ -d ${trunk_dir} ]] && [[ "$(svn status ${trunk_dir}/)" == ""  ]]; then
         palm_svn_update
      else
         rm -rf ${trunk_dir}
         mkdir -p ${install_prefix}
         palm_svn_checkout
      fi
      if [[ "$(svn status ${trunk_dir}/)" != ""  ]]; then
         printf "%s \e[1;31mnot able\e[0m to optain a working copy of ${program_name}. Please optain it manually.\n" "-- Subversion was"
         palm_read_yn "Is there a working copy of ${program_name} in the current directory?" "IS_TRUNK"
         [[ "$IS_TRUNK" != "y" ]] && palm_installer_abort_message
      fi
   fi
   SVN_CMAKE_CHECK="YES"
   palm_svn_check_local
   if [[ -d ${trunk_dir} ]]; then
      if [[ "$SVN_CMAKE_CHECK" == "YES" ]]; then
         if [[ "$(svn status ${trunk_dir}/)" == ""  ]]; then
            local tmp_revision=( $(svn info ${trunk_dir}/ | grep Revision:) )
            local new_revision=${tmp_revision[1]}
            printf "%s \e[1;32mfound\e[0m.\n" "-- ${program_name} (revision: ${new_revision})"
         fi
      else
         printf "%s It is \e[1;33massumed\e[0m a working copy of ${program_name} can be found under: ${trunk_dir}\n" "--"
      fi
   else
      printf "%s \e[1;31m${program_name} was not able to find a working PALM copy. Aborting!\e[0m\n" "--"
      palm_installer_abort_message
   fi
   printf "\n"
   hrule
   printf "| \e[1;32m%-${number_of_cols}s\e[0m |\n" "${program_name} installer initialization done."
   hrule
   palm_read_yn "Are you happy with your choices and ready to continue?" "RESPONSE_CONFIGURE"
   if [[ "$RESPONSE_CONFIGURE" != "y" ]]; then
      palm_installer_abort_message
   fi
}

# check if svn server is reachable
palm_svn_check_remote() {
   svn info --username "$SVN_USER" ${palm_svn_url} > /dev/null
   local status=$?
   if [[ $status -ne 0 ]]; then
      printf "\e[1;31m%-${number_of_cols}s\e[0m \n" "ERROR: svn server not reachable."
      palm_installer_error_message
   fi
}
# check if svn server is reachable
palm_svn_check_local() {
   svn info ${trunk_dir}/ > /dev/null
   local status=$?
   if [[ $status -ne 0 ]]; then
      printf "\e[1;31m%-${number_of_cols}s\e[0m \n" "ERROR: no healthy local svn repository was found."
      printf "As a workaround you could optain a working copy of the ${program_name} trunk without using Subversion.\n"
      printf "Please place this copy under: ${trunk_dir}\n"
      palm_read_yn "Can you ensure a working copy of the ${program_name} trunk can be found?" "RESPONSE_SVN_ERROR"
      if [[ "$RESPONSE_SVN_ERROR" != "y" ]]; then
         palm_installer_abort_message
      fi
      SVN_CMAKE_CHECK="NO"
   fi
}

# get svn username
palm_svn_get_username() {
   for svn_auth_file in ~/.subversion/auth/svn.simple/*; do
      if [[ $(cat ${svn_auth_file} | grep "${palm_base_url}") != "" ]]; then
         cat ${svn_auth_file} | awk '/^K / { key=1; val=0; } /^V / { key=0; val=1; } ! /^[KV] / { if(key) { kname=$1; } if(val && kname == "username") { print $1; val = 0; } }'
         return 0
      fi
   done
   read -p "-- Please enter your ${program_name} svn username: " username
   echo ${username}
   return 0
}

# check if svn server is reachable
palm_svn_newest_revision() {
   local tmp_revision=( $(svn info --username "$SVN_USER" ${palm_svn_url} | grep Revision:) )
   echo ${tmp_revision[1]}
}

# check if svn server is reachable
palm_svn_local_revision() {
   local tmp_revision=( $(svn info ${trunk_dir}/ | grep Revision:) )
   echo ${tmp_revision[1]}
}


palm_svn_checkout() {
   local n=$(($(svn info --username "$SVN_USER" -R ${palm_svn_url} | grep "URL: " | uniq | wc -l)/2))
   printf "%s" "-- Number of files to be downloaded: $n\n"
   local i=1
   while read line filename; do
      if [[ $i -le $n ]]; then
         ((++i))
         local percent=$(printf "%3s" "$(((${i:-0}*100)/${n:-100}))")
         local status_line=$(printf "[ %s%% ] Downloading file: %s" "$percent" "$(basename $filename)")
         printf "\r-- %$(($(tput cols)-3))s" " "
         printf "\r-- %s" "$status_line"
      fi
   done < <(svn checkout --username "$SVN_USER" ${CUSTOM_REVISION:+"-r${CUSTOM_REVISION}"} ${palm_svn_url} ${trunk_dir})
   printf "\r-- %$(($(tput cols)-3))s" " "
   printf "\r-- [ %3s%% ] \e[1;32m%s\e[0m\n" "100" "Download finished!"
}

palm_svn_update() {
   local n=$(($(svn diff -r ${existing_revision}:${CUSTOM_REVISION} --summarize ${palm_svn_url} | uniq | wc -l)/2))
   printf "%s\n" "-- Number of files to be updated: $n"
   local i=1
   while read line filename; do
      if [[ $i -le $n ]]; then
         ((++i))
         local percent=$(printf "%3s" "$(((${i:-0}*100)/${n:-100}))")
         local status_line=$(printf "[ %s%% ] Updating file: %s" "$percent" "$(basename $filename)")
         printf "\r-- %$(($(tput cols)-3))s" " "
         printf "\r-- %s" "$status_line"
      fi
   done < <(svn update ${CUSTOM_REVISION:+"-r${CUSTOM_REVISION}"} ${trunk_dir})
   printf "\r-- %$(($(tput cols)-3))s" " "
   printf "\r-- [ %3s%% ] \e[1;32m%s\e[0m\n" "100" "Update finished!"
}

################################################################################

# startup message
palm_installer_startup_message() {
   printf "\n"
   hrule
   printf "|                                                                        |\n"
   printf "|                       PALM installer (${installer_version})                         |\n"
   printf "|                   https://palm.muk.uni-hannover.de/                    |\n"
   printf "|                                                                        |\n"
}

# shutdown message
palm_installer_shutdown_message() {
   hrule
   printf "| %-${number_of_cols}s |\n" "${program_name} installer finished."
   hrule
   rm -f ${logfile_all}
   exit 0
}

# shutdown message
palm_installer_abort_message() {
   hrule
   printf "| \e[1;31m%-${number_of_cols}s\e[0m |\n" "${program_name} installer aborted."
   hrule
   exit 0
}

# error wrap-up message
palm_installer_error_message() {
   hrule
   printf "| \e[1;31m%-${number_of_cols}s\e[0m \n" "${program_name} installer crashed."
   palm_installer_ticket_message
   hrule
   exit 1
}

palm_installer_ticket_message() {
   printf "| \e[1;31m%-${number_of_cols}s\e[0m \n" " "
   printf "| \e[1;31m%-${number_of_cols}s\e[0m \n" "In order to get help, please use our ticket system at:"
   printf "| \e[1;31m%-${number_of_cols}s\e[0m \n" "   \"https://palm.muk.uni-hannover.de/trac/wiki/help\""
   printf "| \e[1;31m%-${number_of_cols}s\e[0m \n" " "
   printf "| \e[1;31m%-${number_of_cols}s\e[0m \n" "Please allways attach the following file to the ticket:"
   printf "| \e[1;31m%-${number_of_cols}s\e[0m \n" "   \"${logfile_all}\""
}

hrule() {
   printf "#"
   printf -- '-%.0s' {1..72}
   printf "#\n"
}

palm_read_yn() {
   sleep 0.1
   while true; do
   read -p "-- ${1} (y|n): " CURRENT_RESPONSE
   if [[ "${CURRENT_RESPONSE}" == "y" ]] || [[ "${CURRENT_RESPONSE}" == "n" ]]; then
      eval ${2}="${CURRENT_RESPONSE}"
      break
   fi
   done
}

palm_installer_help() {
   hrule
   printf "| %-${number_of_cols}s |\n" "USAGE: bash ${0} [--init|--version|--prep]"
   hrule
   rm -f ${logfile_all}
   exit 0
}

palm_installer_version() {
   hrule
   printf "| %-${number_of_cols}s |\n" "VERSION: This PALM installer has version number: ${installer_version}"
   hrule
   rm -f ${logfile_all}
   exit 0
}

palm_installer_prep_mpi() {
   sudo apt-get install mpich
}

palm_installer_prep_netcdf() {
   sudo apt-get install libnetcdf-dev libnetcdff-dev ncview
}

palm_installer_prep_gt() {
   sudo apt-get install libqt4-dev
}

palm_installer_prep_fftw() {
   sudo apt-get install libfftw3-dev
}

################################################################################

palm_installer_startup_message
palm_installer_update

if [[ "${1}" =~ "--help" ]]; then
   palm_installer_help
fi

if [[ "${1}" =~ "--version" ]]; then
   palm_installer_version
fi

if [[ "${1}" =~ "--prep" ]]; then
   palm_installer_prep_mpi
   palm_installer_prep_netcdf
   palm_installer_prep_gt
   palm_installer_prep_fftw
   palm_installer_shutdown_message
fi

if [[ "${1}" =~ "--rrtmg" ]]; then
   palm_installer_build_rrtmg="true"
else
   palm_installer_build_rrtmg="false"
fi

palm_installer_check_software_all
palm_installer_init

if [[ -f ${trunk_dir}/INSTALL/palm_installer_components ]]; then
   # temporary solution only
   source ${trunk_dir}/INSTALL/palm_installer_components
else
   printf "\e[1;31m%-${number_of_cols}s\e[0m \n" "ERROR: file not found: ${trunk_dir}/INSTALL/palm_installer_components"
   palm_installer_error_message
fi

palm_installer_shutdown_message
