#!/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-2015  Leibniz Universitaet Hannover
#------------------------------------------------------------------------------#
# palm_installer - an automatic installation scipt for PALM
#------------------------------------------------------------------------------#

# script version
installer_version="v0.3.22"

# 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=$(get_number_of_cpu_cores)
palm_base_url="https://palm.muk.uni-hannover.de"
palm_svn_url="${palm_base_url}/svn/palm/trunk"
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 and 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 "MPI" mpiexec mpirun aprun srun
   palm_installer_check_software_optional "svn" "${program_name} has to be downloaded manually" svn
   palm_installer_check_software_required "ksh" ksh
   palm_installer_check_software_required "make" make
   palm_installer_check_software_required "cmake" cmake
   palm_installer_check_software_optional "qmake" "The GUI will not be available" qmake
   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 ifort ftn gfortran mpif90)
   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 [[ -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 (1 to ${newest_revision}): " CUSTOM_REVISION
         while [[ ${CUSTOM_REVISION} -gt ${newest_revision} ]] || [[ ${CUSTOM_REVISION} -lt 1 ]]; do
            printf "%s \e[1;31mnot a valid ${program_name} revision!\e[0m " "--"
            read -p "Please choose a value between 1 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
   if [[ -d ${trunk_dir} ]]; then
      palm_svn_check_local
      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})"
      else
         printf "%s It is \e[1;33massumed\e[0m a working copy of ${program_name} can be found in the current directory.\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: local svn repository is not healthy."
      palm_installer_error_message
   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!"
}

# install PALM
palm_installer_build() {
   printf "%s\n" "${program_name} installer is configuring using cmake..."
   palm_installer_create_files
   rm -rf ${build_dir}
   mkdir -p ${build_dir}
   cd ${build_dir}
   cmake -Wno-dev ${CUSTOM_FORTRAN_COMPILER:+-DCMAKE_Fortran_COMPILER=}${CUSTOM_FORTRAN_COMPILER} \
         ${NETCDF_C_ROOT:+-DNETCDF_C_ROOT=}${NETCDF_C_ROOT} \
         ${NETCDF_FORTRAN_ROOT:+-DNETCDF_FORTRAN_ROOT=}${NETCDF_FORTRAN_ROOT} \
         -DCMAKE_BUILD_TYPE=Release \
         ${install_prefix:+-DCMAKE_INSTALL_PREFIX=}${install_prefix} \
         -DCMAKE_USERNAME=${USER} \
         ${source_dir}
   if [[ $? -ne 0 ]]; then
      hrule
      printf "| \e[1;31m%-${number_of_cols}s\e[0m |\n" "Configuration failed!"
      palm_installer_ticket_message
      hrule
      exit 1
   else
      hrule
      printf "| \e[1;32m%-${number_of_cols}s\e[0m |\n" "Configuration finished!"
      hrule
   fi
   palm_read_yn "Please check the configuration!!! Would you like to continue?" "RESPONSE_CONFIGURE"
   if [[ "$RESPONSE_CONFIGURE" != "y" ]]; then
      palm_installer_abort_message
   fi
   cd ${install_prefix}
   rm -rf ${source_dir}
   rm -rf MAKE_DEPOSITORY_parallel
   ${trunk_dir}/SCRIPTS/mbuild -v -K "parallel" -u
   ${trunk_dir}/SCRIPTS/mbuild -v -K "parallel"
}

palm_installer_set_path() {
   [[ -w ${HOME}/.mybashrc ]] && shell_profile=${HOME}/.mybashrc
   if [[ ! -w ${shell_profile} ]]; then
      printf "\e[1;31mWARNING\e[0m: ${program_name} binary \e[1;31mpath could not be written\e[0m to \"${shell_profile}\"\n"
      printf "In order to run the ${program_name} execution script \"mrun\" you need to set the following paths\n"
      printf "   export PALM_BIN=${trunk_dir}/SCRIPTS\n"
      printf "   export PATH=\$PALM_BIN:\$PATH\n"
   elif grep -q "^export PALM_BIN=${trunk_dir}/SCRIPTS" ${shell_profile}; then
      printf "${program_name} binary \e[1;32mpath already set correctly\e[0m in \"${shell_profile}\"\n"
   else
      if grep -q "PALM_BIN" ${shell_profile}; then
         sed -i "s@^[^#]\(.*PALM_BIN.*\)@#e\1@" ${shell_profile}
         printf "${program_name} binary \e[1;32mpath updated\e[0m in \"${shell_profile}\""
      else
         printf "${program_name} binary \e[1;32mpath written\e[0m to \"${shell_profile}\""
      fi
      cat >> ${shell_profile} << EOF
#-------------------------------------------------------------------------------
##PALM##
export PALM_BIN=${trunk_dir}/SCRIPTS
export PATH=\$PALM_BIN:\$PATH

EOF
   fi
   export PALM_BIN=${trunk_dir}/SCRIPTS
   export PATH=$PALM_BIN:$PATH
   echo " "
}

palm_installer_test() {
   hrule
   printf "Testing ${program_name} with \"${1}\"... "
   source ${shell_profile}
   local input_dir=${install_prefix}/JOBS/${1}/INPUT/
   local monitoring_dir=${install_prefix}/JOBS/${1}/MONITORING/
   local test_dir=${trunk_dir}/INSTALL
   if [[ ! -f ${test_dir}/${1}_p3d ]] || [[ ! -f ${test_dir}/${1}_rc ]]; then
      printf "\e[1;31m test not found\e[0m\n"
      return 1
   fi
   rm -rf ${monitoring_dir}
   mkdir -p ${input_dir}
   cp ${test_dir}/${1}_p3d ${input_dir}
   [[ -f ${test_dir}/${1}_topo ]] && cp ${test_dir}/${1}_topo ${input_dir}
   ${trunk_dir}/SCRIPTS/mrun -d ${1} -r "d3#" -h "lclocal" -K "parallel" -X "$NUM_PROC" -T "$NUM_PROC" -v -B > ${install_prefix}/${logfile_test} 2>&1
   grep -A 99999 "Run-control output" JOBS/${1}/MONITORING/${1}_rc 1> ${install_prefix}/RC_LOCAL 2> /dev/null
   grep -A 99999 "Run-control output" ${test_dir}/${1}_rc 1> ${install_prefix}/RC_DEFAULT 2> /dev/null
   diff_output=$(diff ${install_prefix}/RC_DEFAULT ${install_prefix}/RC_LOCAL)
   rm ${install_prefix}/RC_LOCAL ${install_prefix}/RC_DEFAULT
   if [[ "${diff_output}" == "" ]]; then
      printf "\e[1;32m passed\e[0m\n"
      return 0
   else
      printf "\e[1;31m failed\e[0m\n"
      test_status="failed"
      return 1
   fi
}

palm_installer_test_suite() {
   rm -f ${install_prefix}/${logfile_test}
   for test_path in ${trunk_dir}/INSTALL/*_p3d; do
      testname_p3d=$(basename $test_path)
      palm_installer_test "${testname_p3d%_p3d}"
   done
   hrule
   cat ${install_prefix}/${logfile_test} >> ${logfile_all}
   if [[ "${test_status}" == "failed" ]]; then
      printf "| \e[1;31m%-${number_of_cols}s\e[0m |\n" "Some tests failed. ${program_name} installation not successful :-("
      printf "| \e[1;31m%-${number_of_cols}s\e[0m |\n" "More detailed information regarding the failure can be found in file:"
      printf "| \e[1;31m%-${number_of_cols}s\e[0m |\n" "      ${install_prefix}/${logfile_test}"
      palm_installer_ticket_message
      hrule
      exit 1
   else
      printf "| \e[1;32m%-${number_of_cols}s\e[0m |\n" "All found tests passed. ${program_name} installation successful :-)"
      rm -f ${install_prefix}/${logfile_test}
      rm -rf ${install_prefix}/tmp/*
      hrule
   fi
}

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

# 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_post_installer_help() {
   hrule
   printf "| %-${number_of_cols}s \n" " "
   printf "| %-${number_of_cols}s \n" "-- To use ${program_name} it is required to update your PATH variable. Please type:"
   printf "| %-${number_of_cols}s \n" " "
   printf "| %-${number_of_cols}s \n" "      source ${shell_profile}"
   printf "| %-${number_of_cols}s \n" " "
   printf "| %-${number_of_cols}s \n" "   or restart your shell."
   printf "| %-${number_of_cols}s \n" " "
   printf "| %-${number_of_cols}s \n" "-- To work with ${program_name} please go to your ${program_name} base directory:"
   printf "| %-${number_of_cols}s \n" " "
   printf "| %-${number_of_cols}s \n" "      cd ${install_prefix}"
   printf "| %-${number_of_cols}s \n" " "
   printf "| %-${number_of_cols}s \n" "-- To start the model, please use \"mrun\" with appropriate options. For example:"
   printf "| %-${number_of_cols}s \n" " "
   printf "| %-${number_of_cols}s \n" "      mrun -d example_cbl -r \"d3#\" -h \"lclocal\" -K \"parallel\" -X \"4\" -T \"4\"      "
   printf "| %-${number_of_cols}s \n" "                   |           |          |            |          |      |       "
   printf "| %-${number_of_cols}s \n" "               Job name        |   host identifier     | number of cores |       "
   printf "| %-${number_of_cols}s \n" "                        I/O control list     configuration block   tasks per node"
   printf "| %-${number_of_cols}s \n" " "
   printf "| %-${number_of_cols}s \n" "-- There is a GUI called \"mrungui\" available as well (Qt4 required)."
   printf "| %-${number_of_cols}s \n" " "
   printf "| %-${number_of_cols}s \n" "-- Edit \".mrun.config\" to customize this ${program_name} installation."
   printf "| %-${number_of_cols}s \n" "-- To rebuild ${program_name} type: \"mbuild\"."
   printf "| %-${number_of_cols}s \n" " "
   printf "| %-${number_of_cols}s \n" "-- For further questions go to: https://palm.muk.uni-hannover.de/."
   printf "| %-${number_of_cols}s \n" " "
}

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_create_files() {
   mkdir -p ${source_dir}
   cat > ${source_dir}/.mrun.config.in << EOF
#\$Id: .mrun.config.gfortran 1446 2014-08-07 10:08:56Z knoop $
#column 1          column 2                                   column 3
#name of variable  value of variable (~ must not be used)     scope
#------------------------------------------------------------------------------#
%mainprog          palm.f90
%base_directory    @CMAKE_INSTALL_PREFIX@
%base_data         \$base_directory/JOBS
%source_path       \$base_directory/trunk/SOURCE
%add_source_path   \$base_directory/USER_CODE/\$fname
%depository_path   \$base_directory/MAKE_DEPOSITORY
#
# Enter your own host below by adding another line containing in the second
# column your hostname (as provided by the unix command "hostname") and in the
# third column the host identifier. Depending on your operating system, the
# first characters of the host identifier should be "lc" (Linux cluster), "ibm"
# (IBM-AIX), or "nec" (NEC-SX), respectively.
#
# The next line is just an example. Add your own line below or replace this line.
%host_identifier   @PALM_HOSTNAME@  lclocal
#
#
# The next block contains all informations for compiling the PALM code
# and for generating and running the PALM executable using MPI. Replace all
# required paths (given in <>) by the respective paths valid on your host.
# Also replace lclocal by your host identifier (for example "lcmy") 
# in each line!
# Compilernames, cpp-options and compiler-options are assuming
# an GNU-Compiler and mpich2 on this host! Please change
# appropriately, if you are using a different compiler / MPI-Version.
#
# highly optimized mode
%remote_username   @CMAKE_USERNAME@                       lclocal parallel
%tmp_user_catalog  @CMAKE_INSTALL_PREFIX@/tmp             lclocal parallel
%tmp_data_catalog  @CMAKE_INSTALL_PREFIX@/RESTART_DATA    lclocal parallel
%compiler_name     @MPI_Fortran_COMPILER@                 lclocal parallel
%compiler_name_ser @CMAKE_Fortran_COMPILER@               lclocal parallel
%cpp_options       @PALM_CPP_OPTIONS_STR@                 lclocal parallel
%fopts             @CMAKE_Fortran_FLAGS_RELEASE_STR@      lclocal parallel
%lopts             @CMAKE_Fortran_FLAGS_RELEASE_STR@      lclocal parallel
%mopts             -j:@PALM_CORES@                        lclocal parallel
%netcdf_inc        -I:@NETCDF_INCLUDES@                   lclocal parallel
%netcdf_lib        @NETCDF_LIBRARIES@                     lclocal parallel
%fftw_inc          -I:@FFTW_INCLUDES@                     lclocal parallel
%fftw_lib          @FFTW_LIBRARIES@                       lclocal parallel
# if you want to use your own hostfile, uncomment next line
#%hostfile          \$base_directory/.hostfile             lclocal parallel
# if you want to load modules on the remote host, use the following line
#%modules           <replace_by_the_modules_to_be_loaded> lclocal parallel
# if you want to perform initial commands on ssh connections, use the following line
#%login_init_cmd    <replace_by_the_inital_commands>      lclocal parallel
#
# debug/trace mode
%remote_username   @CMAKE_USERNAME@                       lclocal parallel trace
%tmp_user_catalog  @CMAKE_INSTALL_PREFIX@/tmp             lclocal parallel trace
%tmp_data_catalog  @CMAKE_INSTALL_PREFIX@/RESTART_DATA    lclocal parallel trace
%compiler_name     @MPI_Fortran_COMPILER@                 lclocal parallel trace
%compiler_name_ser @CMAKE_Fortran_COMPILER@               lclocal parallel trace
%cpp_options       @PALM_CPP_OPTIONS_STR@                 lclocal parallel trace
%fopts             @CMAKE_Fortran_FLAGS_DEBUG_STR@        lclocal parallel trace
%lopts             @CMAKE_Fortran_FLAGS_DEBUG_STR@        lclocal parallel trace
%mopts             -j:@PALM_CORES@                        lclocal parallel trace
%netcdf_inc        -I:@NETCDF_INCLUDES@                   lclocal parallel trace
%netcdf_lib        @NETCDF_LIBRARIES@                     lclocal parallel trace
%fftw_inc          -I:@FFTW_INCLUDES@                     lclocal parallel trace
%fftw_lib          @FFTW_LIBRARIES@                       lclocal parallel trace
# if you want to use your own hostfile, uncomment next line
#%hostfile          \$base_directory/.hostfile             lclocal parallel trace
# if you want to load modules on the remote host, use the following line
#%modules           <replace_by_the_modules_to_be_loaded> lclocal parallel trace
# if you want to perform initial commands on ssh connections, use the following line
#%login_init_cmd    <replace_by_the_inital_commands>      lclocal parallel trace
#
#
%write_binary                true                             restart
#
#------------------------------------------------------------------------------#
# INPUT-commands, executed before running PALM - lines must start with "IC:" 
#------------------------------------------------------------------------------#
#IC:
#
#------------------------------------------------------------------------------#
# ERROR-commands - executed when program terminates abnormally
#------------------------------------------------------------------------------#
EC:[[ \\\$locat = execution ]]  &&  cat  RUN_CONTROL
#
#------------------------------------------------------------------------------#
# OUTPUT-commands - executed when program terminates normally
#------------------------------------------------------------------------------#
#
# Combine 1D- and 3D-profile output (these files are not usable for plotting)
OC:[[ -f LIST_PROFIL_1D     ]]  &&  cat  LIST_PROFIL_1D  >>  LIST_PROFILE
OC:[[ -f LIST_PROFIL        ]]  &&  cat  LIST_PROFIL     >>  LIST_PROFILE
#
# Combine all particle information files
OC:[[ -f PARTICLE_INFOS/_0000 ]]  &&  cat  PARTICLE_INFOS/* >> PARTICLE_INFO
#
#------------------------------------------------------------------------------#
# List of input-files
#------------------------------------------------------------------------------#
PARIN                    in:job        d3#          \$base_data/\$fname/INPUT    _p3d
PARIN                    in:job        d3f          \$base_data/\$fname/INPUT    _p3df
rrtmg_lw.nc              in:locopt     d3#:d3f      \$base_data/\$fname/INPUT    _rlw nc
rrtmg_sw.nc              in:locopt     d3#:d3f      \$base_data/\$fname/INPUT    _rsw nc
RAD_SND_DATA             in:locopt     d3#:d3f      \$base_data/\$fname/INPUT    _rsnd nc
TOPOGRAPHY_DATA          in:locopt     d3#:d3f      \$base_data/\$fname/INPUT    _topo
NUDGING_DATA             in:locopt     d3#:d3f      \$base_data/\$fname/INPUT    _nudge
LSF_DATA                 in:locopt     d3#:d3f      \$base_data/\$fname/INPUT    _lsf
BININ                    in:loc:flpe   d3f:cycfill  \$base_data/\$fname/RESTART  _d3d
PARTICLE_RESTART_DATA_IN in:loc:flpe   prtf         \$base_data/\$fname/RESTART  _rprt
DATA_1D_PR_NETCDF        in:locopt     prf          \$base_data/\$fname/OUTPUT   _pr     nc
DATA_1D_SP_NETCDF        in:locopt     spf          \$base_data/\$fname/OUTPUT   _sp     nc
DATA_1D_TS_NETCDF        in:locopt     tsf          \$base_data/\$fname/OUTPUT   _ts     nc
DATA_1D_PTS_NETCDF       in:locopt     ptsf         \$base_data/\$fname/OUTPUT   _pts    nc
DATA_2D_XY_NETCDF        in:locopt     xyf          \$base_data/\$fname/OUTPUT   _xy     nc
DATA_2D_XY_AV_NETCDF     in:locopt     xyf          \$base_data/\$fname/OUTPUT   _xy_av  nc
DATA_2D_XZ_NETCDF        in:locopt     xzf          \$base_data/\$fname/OUTPUT   _xz     nc
DATA_2D_XZ_AV_NETCDF     in:locopt     xzf          \$base_data/\$fname/OUTPUT   _xz_av  nc
DATA_2D_YZ_NETCDF        in:locopt     yzf          \$base_data/\$fname/OUTPUT   _yz     nc
DATA_2D_YZ_AV_NETCDF     in:locopt     yzf          \$base_data/\$fname/OUTPUT   _yz_av  nc
DATA_3D_NETCDF           in:locopt     3df          \$base_data/\$fname/OUTPUT   _3d     nc
DATA_3D_AV_NETCDF        in:locopt     3df          \$base_data/\$fname/OUTPUT   _3d_av  nc
DATA_PRT_NETCDF          in:locopt:pe  prtf         \$base_data/\$fname/OUTPUT   _prt
#
#------------------------------------------------------------------------------#
# List of output-files
#------------------------------------------------------------------------------#
BINOUT                    out:loc:flpe restart   \$base_data/\$fname/RESTART  _d3d
PARTICLE_RESTART_DATA_OUT out:loc:flpe prt#:prtf \$base_data/\$fname/RESTART  _rprt
#
RUN_CONTROL       out:loc:tr   d3#     \$base_data/\$fname/MONITORING  _rc
RUN_CONTROL       out:loc:tra  d3f     \$base_data/\$fname/MONITORING  _rc
HEADER            out:loc:tr   d3#     \$base_data/\$fname/MONITORING  _header
HEADER            out:loc:tra  d3f     \$base_data/\$fname/MONITORING  _header
CPU_MEASURES      out:loc:tr   d3#     \$base_data/\$fname/MONITORING  _cpu
CPU_MEASURES      out:loc:tra  d3f     \$base_data/\$fname/MONITORING  _cpu
#
DATA_1D_PR_NETCDF    out:loc:tr   pr#:prf   \$base_data/\$fname/OUTPUT  _pr     nc
DATA_1D_SP_NETCDF    out:loc:tr   sp#:spf   \$base_data/\$fname/OUTPUT  _sp     nc
DATA_1D_TS_NETCDF    out:loc:tr   ts#:tsf   \$base_data/\$fname/OUTPUT  _ts     nc
DATA_1D_PTS_NETCDF   out:loc:tr   pts#:ptsf \$base_data/\$fname/OUTPUT  _pts    nc
DATA_2D_XY_NETCDF    out:loc:tr   xy#:xyf   \$base_data/\$fname/OUTPUT  _xy     nc
DATA_2D_XY_AV_NETCDF out:loc:tr   xy#:xyf   \$base_data/\$fname/OUTPUT  _xy_av  nc
DATA_2D_XZ_NETCDF    out:loc:tr   xz#:xzf   \$base_data/\$fname/OUTPUT  _xz     nc
DATA_2D_XZ_AV_NETCDF out:loc:tr   xz#:xzf   \$base_data/\$fname/OUTPUT  _xz_av  nc
DATA_2D_YZ_NETCDF    out:loc:tr   yz#:yzf   \$base_data/\$fname/OUTPUT  _yz     nc
DATA_2D_YZ_AV_NETCDF out:loc:tr   yz#:yzf   \$base_data/\$fname/OUTPUT  _yz_av  nc
DATA_3D_NETCDF       out:loc:tr   3d#:3df   \$base_data/\$fname/OUTPUT  _3d     nc
DATA_3D_AV_NETCDF    out:loc:tr   3d#:3df   \$base_data/\$fname/OUTPUT  _3d_av  nc
DATA_MASK_01_NETCDF    out:loc:tr ma#:maf   \$base_data/\$fname/OUTPUT  _m01     nc
DATA_MASK_01_AV_NETCDF out:loc:tr ma#:maf   \$base_data/\$fname/OUTPUT  _m01_av  nc
DATA_MASK_02_NETCDF    out:loc:tr ma#:maf   \$base_data/\$fname/OUTPUT  _m02     nc
DATA_MASK_02_AV_NETCDF out:loc:tr ma#:maf   \$base_data/\$fname/OUTPUT  _m02_av  nc
DATA_MASK_03_NETCDF    out:loc:tr ma#:maf   \$base_data/\$fname/OUTPUT  _m03     nc
DATA_MASK_03_AV_NETCDF out:loc:tr ma#:maf   \$base_data/\$fname/OUTPUT  _m03_av  nc
DATA_MASK_04_NETCDF    out:loc:tr ma#:maf   \$base_data/\$fname/OUTPUT  _m04     nc
DATA_MASK_04_AV_NETCDF out:loc:tr ma#:maf   \$base_data/\$fname/OUTPUT  _m04_av  nc
DATA_MASK_05_NETCDF    out:loc:tr ma#:maf   \$base_data/\$fname/OUTPUT  _m05     nc
DATA_MASK_05_AV_NETCDF out:loc:tr ma#:maf   \$base_data/\$fname/OUTPUT  _m05_av  nc
DATA_MASK_06_NETCDF    out:loc:tr ma#:maf   \$base_data/\$fname/OUTPUT  _m06     nc
DATA_MASK_06_AV_NETCDF out:loc:tr ma#:maf   \$base_data/\$fname/OUTPUT  _m06_av  nc
DATA_MASK_07_NETCDF    out:loc:tr ma#:maf   \$base_data/\$fname/OUTPUT  _m07     nc
DATA_MASK_07_AV_NETCDF out:loc:tr ma#:maf   \$base_data/\$fname/OUTPUT  _m07_av  nc
DATA_MASK_08_NETCDF    out:loc:tr ma#:maf   \$base_data/\$fname/OUTPUT  _m08     nc
DATA_MASK_08_AV_NETCDF out:loc:tr ma#:maf   \$base_data/\$fname/OUTPUT  _m08_av  nc
DATA_MASK_09_NETCDF    out:loc:tr ma#:maf   \$base_data/\$fname/OUTPUT  _m09     nc
DATA_MASK_09_AV_NETCDF out:loc:tr ma#:maf   \$base_data/\$fname/OUTPUT  _m09_av  nc
DATA_MASK_10_NETCDF    out:loc:tr ma#:maf   \$base_data/\$fname/OUTPUT  _m10     nc
DATA_MASK_10_AV_NETCDF out:loc:tr ma#:maf   \$base_data/\$fname/OUTPUT  _m10_av  nc
DATA_MASK_11_NETCDF    out:loc:tr ma#:maf   \$base_data/\$fname/OUTPUT  _m11     nc
DATA_MASK_11_AV_NETCDF out:loc:tr ma#:maf   \$base_data/\$fname/OUTPUT  _m11_av  nc
DATA_MASK_12_NETCDF    out:loc:tr ma#:maf   \$base_data/\$fname/OUTPUT  _m12     nc
DATA_MASK_12_AV_NETCDF out:loc:tr ma#:maf   \$base_data/\$fname/OUTPUT  _m12_av  nc
DATA_MASK_13_NETCDF    out:loc:tr ma#:maf   \$base_data/\$fname/OUTPUT  _m13     nc
DATA_MASK_13_AV_NETCDF out:loc:tr ma#:maf   \$base_data/\$fname/OUTPUT  _m13_av  nc
DATA_MASK_14_NETCDF    out:loc:tr ma#:maf   \$base_data/\$fname/OUTPUT  _m14     nc
DATA_MASK_14_AV_NETCDF out:loc:tr ma#:maf   \$base_data/\$fname/OUTPUT  _m14_av  nc
DATA_MASK_15_NETCDF    out:loc:tr ma#:maf   \$base_data/\$fname/OUTPUT  _m15     nc
DATA_MASK_15_AV_NETCDF out:loc:tr ma#:maf   \$base_data/\$fname/OUTPUT  _m15_av  nc
DATA_MASK_16_NETCDF    out:loc:tr ma#:maf   \$base_data/\$fname/OUTPUT  _m16     nc
DATA_MASK_16_AV_NETCDF out:loc:tr ma#:maf   \$base_data/\$fname/OUTPUT  _m16_av  nc
DATA_MASK_17_NETCDF    out:loc:tr ma#:maf   \$base_data/\$fname/OUTPUT  _m17     nc
DATA_MASK_17_AV_NETCDF out:loc:tr ma#:maf   \$base_data/\$fname/OUTPUT  _m17_av  nc
DATA_MASK_18_NETCDF    out:loc:tr ma#:maf   \$base_data/\$fname/OUTPUT  _m18     nc
DATA_MASK_18_AV_NETCDF out:loc:tr ma#:maf   \$base_data/\$fname/OUTPUT  _m18_av  nc
DATA_MASK_19_NETCDF    out:loc:tr ma#:maf   \$base_data/\$fname/OUTPUT  _m19     nc
DATA_MASK_19_AV_NETCDF out:loc:tr ma#:maf   \$base_data/\$fname/OUTPUT  _m19_av  nc
DATA_MASK_20_NETCDF    out:loc:tr ma#:maf   \$base_data/\$fname/OUTPUT  _m20     nc
DATA_MASK_20_AV_NETCDF out:loc:tr ma#:maf   \$base_data/\$fname/OUTPUT  _m20_av  nc
DATA_PRT_NETCDF      out:loc:trpe prt#:prtf \$base_data/\$fname/OUTPUT  _prt    nc
DATA_DVR             out:loc:trpe dvr       \$base_data/\$fname/OUTPUT  _dvr 
#
DVRP_LOG          out:loc:tr   dv#       \$base_data/\$fname/MONITORING  _dvrp_log
DVRP_LOG          out:loc:tra  dvf       \$base_data/\$fname/MONITORING  _dvrp_log
PARTICLE_INFO     out:loc:tr   pt#       \$base_data/\$fname/MONITORING  _prt_info
PARTICLE_INFO     out:loc:tra  ptf       \$base_data/\$fname/MONITORING  _prt_info
PARTICLE_DATA     out:loc:flpe prt#:prtf \$base_data/\$fname/OUTPUT      _prt_dat
#
#------------------------------------------------------------------------------#
# A BLANK LINE MUST FOLLOW

EOF

   cat > ${source_dir}/CMakeLists.txt << EOF
# This is the CMake configuration file for PALM

cmake_minimum_required (VERSION 2.8)

# set program name
set(PALM_PROGRAM_NAME "PALM")

project(\${PALM_PROGRAM_NAME} NONE)
exec_program( grep ARGS "-c ^processor /proc/cpuinfo" OUTPUT_VARIABLE PALM_CORES)
exec_program( hostname OUTPUT_VARIABLE PALM_HOSTNAME)

# .mrun.config locations
set(config_in \${CMAKE_CURRENT_SOURCE_DIR}/.mrun.config.in)
set(config \${CMAKE_INSTALL_PREFIX}/.mrun.config)

# include local cmake files
set(PALM_CMAKE_FILES \${CMAKE_CURRENT_SOURCE_DIR})
list(APPEND CMAKE_MODULE_PATH \${PALM_CMAKE_FILES})

# enabling Fortran language support
enable_language(Fortran) # required to compile the main model and all utilities

# check for Fortran MPI support
find_package(MPI REQUIRED)

# enabling C++ language support
enable_language(CXX)     # required for MPI and to compile the mrungui

# check for netCDF
#set(NETCDF_C "YES")
set(NETCDF_FORTRAN "YES")
find_package (NetCDF REQUIRED)
find_program(NETCDF_FORTRAN_COMPILER_FULL NAMES \${NETCDF_FORTRAN_COMPILER})
if(NOT \${CMAKE_Fortran_COMPILER} STREQUAL \${NETCDF_FORTRAN_COMPILER_FULL} )
   message(WARNING "Fortran compiler \"\${CMAKE_Fortran_COMPILER}\" does not match netCDF Fortran compiler \"\${NETCDF_FORTRAN_COMPILER_FULL}\".")
else()
   message(STATUS "Fortran compiler matches netCDF Fortran compiler.")
endif()

# extract subversion info
find_package(Subversion)
if(SUBVERSION_FOUND)
   Subversion_WC_INFO(\${PROJECT_SOURCE_DIR}/../trunk PALM)
   message(STATUS "Your \${PALM_PROGRAM_NAME} installation will be based on revision \${PALM_WC_REVISION}.")
else(SUBVERSION_FOUND)
   message(WARNING "Subversion not found. Wondering how you aquired the PALM code. :-)")
endif(SUBVERSION_FOUND)

# check for fftw
find_package(FFTW)
if(FFTW_FOUND)
   message(STATUS "\${PALM_PROGRAM_NAME} is using an external fftw library.")
   list(APPEND PALM_CPP_OPTIONS __fftw)
else(FFTW_FOUND)
   message(STATUS "\${PALM_PROGRAM_NAME} is using the buildin fft algorithm.")
endif(FFTW_FOUND)

# find qt4
find_package(Qt4)

# compiler flag management
if(\${CMAKE_Fortran_COMPILER_ID} STREQUAL "Cray")
   set(CMAKE_Fortran_FLAGS_RELEASE "-em -K trap=fp -O3 -hnoomp -hnoacc -hfp3 -hdynamic -dynamic")
   string(REPLACE " " ":" CMAKE_Fortran_FLAGS_RELEASE_STR "\${CMAKE_Fortran_FLAGS_RELEASE}")
   set(CMAKE_Fortran_FLAGS_DEBUG "-eD -em -g -R b -K trap=fp -O0 -hnoomp -hnoacc")
   string(REPLACE " " ":" CMAKE_Fortran_FLAGS_DEBUG_STR "\${CMAKE_Fortran_FLAGS_DEBUG}")
   set(PALM_CPP_FLAGS "-eZ")
elseif(\${CMAKE_Fortran_COMPILER_ID} STREQUAL "Intel")
   set(CMAKE_Fortran_FLAGS_RELEASE "-O3 -fp-model source -fno-alias -fpe0 -ftz -no-prec-div -no-prec-sqrt -ip -nbs -diag-disable 8290,8291")
   string(REPLACE " " ":" CMAKE_Fortran_FLAGS_RELEASE_STR "\${CMAKE_Fortran_FLAGS_RELEASE}")
   set(CMAKE_Fortran_FLAGS_DEBUG " -O0 -g -debug -traceback -C -w -xT -check nooutput_conversion -convert little_endian -fno-alias -fpe0 -ftz -no-prec-div -no-prec-sqrt -ip -nbs -diag-disable 8290,8291")
   string(REPLACE " " ":" CMAKE_Fortran_FLAGS_DEBUG_STR "\${CMAKE_Fortran_FLAGS_DEBUG}")
   set(PALM_CPP_FLAGS "-cpp")
elseif(\${CMAKE_Fortran_COMPILER_ID} STREQUAL "GNU")
   set(CMAKE_Fortran_FLAGS_RELEASE "-Ofast -ffree-line-length-none")
   string(REPLACE " " ":" CMAKE_Fortran_FLAGS_RELEASE_STR "\${CMAKE_Fortran_FLAGS_RELEASE}")
   set(CMAKE_Fortran_FLAGS_DEBUG "-g -Wall -Wextra -fbacktrace -ffree-line-length-none")
   string(REPLACE " " ":" CMAKE_Fortran_FLAGS_DEBUG_STR "\${CMAKE_Fortran_FLAGS_DEBUG}")
   set(PALM_CPP_FLAGS "-cpp")
endif()

# adding cpp options
list(APPEND PALM_CPP_OPTIONS MPI_REAL=MPI_DOUBLE_PRECISION)
list(APPEND PALM_CPP_OPTIONS MPI_2REAL=MPI_2DOUBLE_PRECISION)
if(NETCDF_FOUND)
   list(APPEND PALM_CPP_OPTIONS __netcdf)
endif()

string(REPLACE ";" ":-D" PALM_CPP_OPTIONS_STR_1 "\${PALM_CPP_OPTIONS}")
set(PALM_CPP_OPTIONS_STR "\${PALM_CPP_FLAGS}:-D\${PALM_CPP_OPTIONS_STR_1}")

# configuring the .mrun.config
configure_file(\${config_in} \${config} @ONLY)

if(NOT FFTW_FOUND)
   exec_program(sed ARGS -i '/%fftw_inc.*/d' \${config} OUTPUT_VARIABLE dummy)
   exec_program(sed ARGS -i '/%fftw_lib.*/d' \${config} OUTPUT_VARIABLE dummy)
endif(FFTW_FOUND)

EOF

   cat > ${source_dir}/FindFFTW.cmake << EOF
# - Find FFTW
# Find the native FFTW includes and library
#
#  FFTW_INCLUDES    - where to find fftw3.h
#  FFTW_LIBRARIES   - List of libraries when using FFTW.
#  FFTW_FOUND       - True if FFTW found.

if (FFTW_INCLUDES)
  # Already in cache, be silent
  set (FFTW_FIND_QUIETLY TRUE)
endif (FFTW_INCLUDES)

#exec_program(sed ARGS -i '/%fftw_inc.*/d' \${config})
#list(APPEND NETCDF_HINTS "\$ENV{LD_LIBRARY_PATH}")

find_path (FFTW_INCLUDES fftw3.f03 HINTS \${NETCDF_HINTS} ENV LD_LIBRARY_PATH  PATH_SUFFIXES include Include)
find_library(NETCDF_FORTRAN_LIB netcdff HINTS \${NETCDF_HINTS} ENV LD_LIBRARY_PATH PATH_SUFFIXES lib lib64)

find_library (FFTW_LIBRARIES NAMES fftw3)

# handle the QUIETLY and REQUIRED arguments and set FFTW_FOUND to TRUE if
# all listed variables are TRUE
include (FindPackageHandleStandardArgs)
find_package_handle_standard_args (FFTW DEFAULT_MSG FFTW_LIBRARIES FFTW_INCLUDES)

mark_as_advanced (FFTW_LIBRARIES FFTW_INCLUDES)

EOF

   cat > ${source_dir}/FindNetCDF.cmake << EOF
# - Find NetCDF
# Find the native NetCDF includes and library
#
#  NETCDF_INCLUDES    - where to find netcdf.h, etc
#  NETCDF_LIBRARIES   - Link these libraries when using NetCDF
#  NETCDF_FOUND       - True if NetCDF found including required interfaces (see below)
#
# Your package can require certain interfaces to be FOUND by setting these
#
#  NETCDF_C           - require the C interface and link the C library
#  NETCDF_CXX         - require the C++ interface and link the C++ library
#  NETCDF_FORTRAN     - require the Fortran interface and link the Fortran library
#
# The following are not for general use and are included in
# NETCDF_LIBRARIES if the corresponding option above is set.
#
#  NETCDF_LIBRARIES_C        - Just the C interface
#  NETCDF_LIBRARIES_CXX      - C++ interface, if available
#  NETCDF_LIBRARIES_FORTRAN  - Fortran 90 interface, if available
#
# Normal usage would be:
#  set (NETCDF_FORTRAN "YES")
#  find_package (NetCDF REQUIRED)
#  target_link_libraries (uses_f90_interface \${NETCDF_LIBRARIES})

if (NETCDF_INCLUDES AND NETCDF_LIBRARIES)
  # Already in cache, be silent
  set (NETCDF_FIND_QUIETLY TRUE)
endif (NETCDF_INCLUDES AND NETCDF_LIBRARIES)


macro(NETCDF_CONFIG flag output)
    if(NETCDF_CONFIG_EXECUTABLE)
        exec_program( \${NETCDF_CONFIG_EXECUTABLE} ARGS \${flag}
            OUTPUT_VARIABLE \${output} RETURN_VALUE return_value)
        if(NOT \${return_value} EQUAL 0 )
            message( STATUS "Unable to determine \${flag} from \${NETCDF_CONFIG_EXECUTABLE}." )
        endif()
    endif(NETCDF_CONFIG_EXECUTABLE)
endmacro()

if(NETCDF_C_ROOT)
   list(APPEND NETCDF_HINTS "\${NETCDF_C_ROOT}")
else()
   list(APPEND NETCDF_HINTS "\$ENV{NETCDF_ROOT}")
endif()

if(NETCDF_FORTRAN_ROOT)
   list(APPEND NETCDF_HINTS "\${NETCDF_FORTRAN_ROOT}")
else()
   list(APPEND NETCDF_HINTS "\$ENV{NETCDF_ROOT}")
endif()

if(NETCDF_C_ROOT)
   find_program(NETCDF_C_CONFIG_EXECUTABLE NAMES nc-config
      HINTS \${NETCDF_HINTS} PATH_SUFFIXES bin Bin NO_DEFAULT_PATH
      DOC "NETCDF CONFIG PROGRAM. Used to detect NETCDF compile flags." )
else()
   find_program(NETCDF_C_CONFIG_EXECUTABLE NAMES nc-config
      HINTS \${NETCDF_HINTS} PATH_SUFFIXES bin Bin
      DOC "NETCDF CONFIG PROGRAM. Used to detect NETCDF compile flags." )
endif()

set(NETCDF_CONFIG_EXECUTABLE \${NETCDF_C_CONFIG_EXECUTABLE})
   if(NETCDF_C_CONFIG_EXECUTABLE)
      NETCDF_CONFIG(--cc NETCDF_C_COMPILER_C)
      NETCDF_CONFIG(--fc NETCDF_C_COMPILER_FORTRAN)
      NETCDF_CONFIG(--prefix NETCDF_C_ROOT)
      NETCDF_CONFIG(--includedir NETCDF_C_INCLUDE)
      NETCDF_CONFIG(--version NETCDF_C_VERSION)
      #NETCDF_CONFIG(--has-c++ NETCDF_C_CXX)
      #NETCDF_CONFIG(--has-f77 NETCDF_C_F77)
      NETCDF_CONFIG(--has-f90 NETCDF_C_F90)
      #NETCDF_CONFIG(--has-dap NETCDF_C_DAP)
      #NETCDF_CONFIG(--has-nc2 NETCDF_C_NC2)
      #NETCDF_CONFIG(--has-nc4 NETCDF_C_NC4)
      #NETCDF_CONFIG(--has-hdf4 NETCDF_C_HDF4)
      #NETCDF_CONFIG(--has-hdf5 NETCDF_C_HDF5)
      #NETCDF_CONFIG(--has-pnetcdf NETCDF_C_PARALLEL)
      list(APPEND NETCDF_INCLUDE_HINTS "\${NETCDF_C_INCLUDE}")
      list(APPEND NETCDF_HINTS "\${NETCDF_C_ROOT}")
      message(STATUS "Found \${NETCDF_C_VERSION} compiled with \${NETCDF_C_COMPILER_C}")
   else(NETCDF_C_CONFIG_EXECUTABLE)
      message(STATUS "nc-config not found")
   endif(NETCDF_C_CONFIG_EXECUTABLE)

if(NETCDF_C_ROOT AND NETCDF_FORTRAN_ROOT)
   find_program(NETCDF_FORTRAN_CONFIG_EXECUTABLE NAMES nf-config
       HINTS \${NETCDF_HINTS} PATH_SUFFIXES bin Bin NO_DEFAULT_PATH
       DOC "NETCDF CONFIG PROGRAM. Used to detect NETCDF compile flags." )
else()
   find_program(NETCDF_FORTRAN_CONFIG_EXECUTABLE NAMES nf-config
       HINTS \${NETCDF_HINTS} PATH_SUFFIXES bin Bin
       DOC "NETCDF CONFIG PROGRAM. Used to detect NETCDF compile flags." )
endif()

set(NETCDF_CONFIG_EXECUTABLE \${NETCDF_FORTRAN_CONFIG_EXECUTABLE})
   if(NETCDF_FORTRAN_CONFIG_EXECUTABLE)
      NETCDF_CONFIG(--cc NETCDF_FORTRAN_COMPILER_C)
      NETCDF_CONFIG(--fc NETCDF_FORTRAN_COMPILER_FORTRAN)
      NETCDF_CONFIG(--prefix NETCDF_FORTRAN_ROOT)
      NETCDF_CONFIG(--includedir NETCDF_FORTRAN_INCLUDE)
      NETCDF_CONFIG(--version NETCDF_FORTRAN_VERSION)
      #NETCDF_CONFIG(--has-c++ NETCDF_FORTRAN_CXX)
      #NETCDF_CONFIG(--has-f77 NETCDF_FORTRAN_F77)
      NETCDF_CONFIG(--has-f90 NETCDF_FORTRAN_F90)
      #NETCDF_CONFIG(--has-dap NETCDF_FORTRAN_DAP)
      #NETCDF_CONFIG(--has-nc2 NETCDF_FORTRAN_NC2)
      #NETCDF_CONFIG(--has-nc4 NETCDF_FORTRAN_NC4)
      #NETCDF_CONFIG(--has-hdf4 NETCDF_FORTRAN_HDF4)
      #NETCDF_CONFIG(--has-hdf5 NETCDF_FORTRAN_HDF5)
      #NETCDF_CONFIG(--has-pnetcdf NETCDF_FORTRAN_PARALLEL)
      list(APPEND NETCDF_INCLUDE_HINTS "\${NETCDF_FORTRAN_INCLUDE}")
      list(APPEND NETCDF_HINTS "\${NETCDF_FORTRAN_ROOT}")
      message(STATUS "Found \${NETCDF_FORTRAN_VERSION} compiled with \${NETCDF_FORTRAN_COMPILER_FORTRAN}")
   else(NETCDF_FORTRAN_CONFIG_EXECUTABLE)
      #message(STATUS "nf-config not found")
      set(NETCDF_FORTRAN_COMPILER_C \${NETCDF_C_COMPILER_C})
      set(NETCDF_FORTRAN_COMPILER_FORTRAN \${NETCDF_C_COMPILER_FORTRAN})
      set(NETCDF_FORTRAN_ROOT \${NETCDF_C_ROOT})
      set(NETCDF_FORTRAN_INCLUDE \${NETCDF_C_INCLUDE})
      set(NETCDF_FORTRAN_VERSION \${NETCDF_C_VERSION})
      #set(NETCDF_FORTRAN_CXX \${NETCDF_C_CXX})
      #set(NETCDF_FORTRAN_F77 \${NETCDF_C_F77})
      set(NETCDF_FORTRAN_F90 \${NETCDF_C_F90})
      #set(NETCDF_FORTRAN_DAP \${NETCDF_C_DAP})
      #set(NETCDF_FORTRAN_NC2 \${NETCDF_C_NC2})
      #set(NETCDF_FORTRAN_NC4 \${NETCDF_C_NC4})
      #set(NETCDF_FORTRAN_HDF4 \${NETCDF_C_HDF4})
      #set(NETCDF_FORTRAN_HDF5 \${NETCDF_C_HDF5})
      #set(NETCDF_FORTRAN_PARALLEL \${NETCDF_C_PARALLEL})
      if(NETCDF_FORTRAN_F90)
         message(STATUS "Found \${NETCDF_FORTRAN_VERSION} compiled with \${NETCDF_FORTRAN_COMPILER_FORTRAN}")
      else(NETCDF_FORTRAN_F90)
         message(STATUS "nc-config found no netCDF Fortran libraries")
      endif(NETCDF_FORTRAN_F90)
   endif(NETCDF_FORTRAN_CONFIG_EXECUTABLE)

# find netcdf c
if(NOT NETCDF_C_INCLUDE)
   find_path(NETCDF_C_INCLUDE netcdf.h HINTS \${NETCDF_HINTS} PATH_SUFFIXES include Include)
endif()
find_library(NETCDF_C_LIB netcdf HINTS \${NETCDF_HINTS} PATH_SUFFIXES lib lib64)
#message(STATUS "NETCDF_C_INCLUDE so far: \${NETCDF_C_INCLUDE}")
#message(STATUS "NETCDF_C_LIB so far: \${NETCDF_C_LIB}")

# find netcdf fortran
if(NOT NETCDF_FORTRAN_INCLUDE)
   find_path(NETCDF_FORTRAN_INCLUDE netcdf.mod HINTS \${NETCDF_HINTS} PATH_SUFFIXES include Include)
endif()
find_library(NETCDF_FORTRAN_LIB netcdff HINTS \${NETCDF_HINTS} PATH_SUFFIXES lib lib64)
if(NOT NETCDF_FORTRAN_LIB)
   find_library(NETCDF_FORTRAN_LIB netcdf HINTS \${NETCDF_HINTS} PATH_SUFFIXES lib lib64)
endif()
#message(STATUS "NETCDF_FORTRAN_INCLUDE so far: \${NETCDF_FORTRAN_INCLUDE}")
#message(STATUS "NETCDF_FORTRAN_LIB so far: \${NETCDF_FORTRAN_LIB}")

if ((NOT NETCDF_C_LIB) OR (NOT NETCDF_C_INCLUDE))
   message(STATUS "Trying to find NetCDF using LD_LIBRARY_PATH (we're desperate)...")
   file(TO_CMAKE_PATH "\$ENV{LD_LIBRARY_PATH}" LD_LIBRARY_PATH)
   find_library(NETCDF_C_LIB NAMES netcdf HINTS \${LD_LIBRARY_PATH})

   if (NETCDF_C_LIB)
      get_filename_component(NETCDF_LIB_DIR \${NETCDF_C_LIB} PATH)
      string(REGEX REPLACE "/lib/?\$" "/include" NETCDF_H_HINT \${NETCDF_LIB_DIR})
      find_path (NETCDF_C_INCLUDE netcdf.h HINTS \${NETCDF_H_HINT} DOC "Path to netcdf.h")
      message(STATUS "found netcdf.h in: \${NETCDF_C_INCLUDE}")
      list(APPEND NETCDF_INCLUDE_HINTS "\${NETCDF_C_INCLUDE}")
   endif()
endif()

get_filename_component (NETCDF_C_LIB_DIR "\${NETCDF_C_LIB}" PATH)
get_filename_component (NETCDF_FORTRAN_LIB_DIR "\${NETCDF_FORTRAN_LIB}" PATH)
list(APPEND NETCDF_LIB_HINTS "\${NETCDF_C_LIB_DIR}")
list(APPEND NETCDF_LIB_HINTS "\${NETCDF_FORTRAN_LIB_DIR}")

#message(STATUS "All include Hints: \${NETCDF_INCLUDE_HINTS}")
#message(STATUS "All lib Hints: \${NETCDF_LIB_HINTS}")

macro(NetCDF_add_interface lang)
   if(NETCDF_\${lang})
      if(NETCDF_\${lang}_INCLUDE AND NETCDF_\${lang}_LIB)
         list(INSERT NetCDF_includes 0 \${NETCDF_\${lang}_INCLUDE})
         list(INSERT NetCDF_libs 0 \${NETCDF_\${lang}_LIB}) # prepend so that -lnetcdf is last
      else()
         set(NetCDF_has_interfaces "NO")
         message(STATUS "Failed to find NetCDF interface for \${lang}")
      endif()
   endif(NETCDF_\${lang})
endmacro(NetCDF_add_interface)

set(NetCDF_has_interfaces "YES") # will be set to NO if we're missing any interfaces
NetCDF_add_interface(C)
NetCDF_add_interface(CXX)
NetCDF_add_interface(FORTRAN)

# macro (NetCDF_check_interface lang header libs)
#    if (NETCDF_\${lang})
#       find_path (NETCDF_INCLUDES_\${lang} NAMES \${header} HINTS \${NETCDF_HINTS} PATH_SUFFIXES include Include NO_DEFAULT_PATH)
#       find_library (NETCDF_LIBRARIES_\${lang} NAMES \${libs} HINTS \${NETCDF_HINTS} PATH_SUFFIXES lib lib64 NO_DEFAULT_PATH)
#       mark_as_advanced (NETCDF_INCLUDES_\${lang} NETCDF_LIBRARIES_\${lang})
#       if (NETCDF_INCLUDES_\${lang} AND NETCDF_LIBRARIES_\${lang})
#          list (INSERT NetCDF_libs 0 \${NETCDF_LIBRARIES_\${lang}}) # prepend so that -lnetcdf is last
#       else (NETCDF_INCLUDES_\${lang} AND NETCDF_LIBRARIES_\${lang})
#          set (NetCDF_has_interfaces "NO")
#          message (STATUS "Failed to find NetCDF interface for \${lang}")
#       endif (NETCDF_INCLUDES_\${lang} AND NETCDF_LIBRARIES_\${lang})
#    endif (NETCDF_\${lang})
# endmacro (NetCDF_check_interface)
# 
# set (NetCDF_has_interfaces "YES") # will be set to NO if we're missing any interfaces
# NetCDF_check_interface (C netcdf.h netcdf)
# NetCDF_check_interface (CXX netcdfcpp.h netcdf_c++)
# NetCDF_check_interface (FORTRAN netcdf.mod  netcdff)

set (NETCDF_C_COMPILER "\${NETCDF_C_COMPILER_C}" CACHE STRING "The C compiler used to build netCDF")
set (NETCDF_FORTRAN_COMPILER "\${NETCDF_FORTRAN_COMPILER_FORTRAN}" CACHE STRING "The Fortran compiler used to build netCDF")
set (NETCDF_INCLUDES "\${NetCDF_includes}" CACHE STRING "All NetCDF includes required for interface level")
set (NETCDF_LIBRARIES "\${NetCDF_libs}" CACHE STRING "All NetCDF libraries required for interface level")

# handle the QUIETLY and REQUIRED arguments and set NETCDF_FOUND to TRUE if
# all listed variables are TRUE
include (FindPackageHandleStandardArgs)
find_package_handle_standard_args (NetCDF DEFAULT_MSG NETCDF_LIBRARIES NETCDF_INCLUDES NetCDF_has_interfaces)

mark_as_advanced (NETCDF_LIBRARIES NETCDF_INCLUDES)

#message(STATUS "netCDF library: \${NETCDF_LIBRARIES}")
#message(STATUS "netCDF include: \${NETCDF_INCLUDES}")

EOF
}

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

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

palm_installer_check_software_all
palm_installer_init
palm_installer_build
palm_installer_set_path
palm_installer_test_suite
palm_post_installer_help
palm_installer_shutdown_message