#!/usr/bin/env bash
# Written by DrSlony
# buildRT version 4.1, 2014-04-01
# Please report bugs or enhancements to http://code.google.com/p/rawtherapee/issues/list
# www.rawtherapee.com
# www.londonlight.org

head -n 4 $0 | tail -n 2 | sed $'1s/.\+/\E[1m&\E[0m/'
echo

if [[ $UID -eq 0 ]]; then
  printf "%s\n" "Do not run this script as root!" "Aborting"
  exit 1
fi

alert () {
  case "$alert_type" in
    notify-send) notify-send "RawTherapee" "$1" ;;
        kdialog) kdialog --title "RawTherapee" --passivepopup "$(printf "%b\n" "$1")" ;;
         zenity) zenity --notification --text="$(printf "%b\n" "$1")" ;;
       xmessage) xmessage -nearmouse "$(printf "%b\n" "$1")" ;;
           none) printf "%b\n" "" "Compilation complete:" "$1" ;;
esac
}

#--- Set some variables
unset choiceNumber choiceNumbers buildType buildTypes list branch branches repo
version="4.1"
movetoPatched=""
repo="${HOME}/rawtherapee"
procTarget=2

while getopts "bc:fnp:s:t:uvh?-" opt; do
    case "${opt}" in
        b)  patched="yes"
            movetoPatched="_patched"
            printf "%s\n" "Buildonly flag detected, will not hg pull or update" ;;
        c)  dCacheNameSuffix="$OPTARG"
            dCacheNameSuffix=${dCacheNameSuffix//[^\.\-_a-zA-Z0-9]/};
            forceCmake="yes"
            printf "%s\n" "Cache and config name suffix: $dCacheNameSuffix" ;;
        f)  forceCmake="yes"
            printf "%s\n" "Will forcefully re-run CMake" ;;
        n)  noomp="-DOPTION_OMP=OFF"
            forceCmake="yes"
            printf "%s\n" "OpenMP disabled" ;;
        p)  procTarget="$OPTARG"
            if [[ $procTarget -lt 1 || $procTarget -gt 9 ]]; then
                printf "%s\n" "Invalid processor target value." "Use a value from 1 to 9, e.g." "./buildRT -p 1" "See ProcessorTargets.cmake" "Aborting"
                exit 1
            forceCmake="yes"
            fi ;;
        s)  movetoPatched="_${OPTARG//[^\.\-_a-zA-Z0-9]/}"
            printf "%s\n" "Suffix of destination build dir: ${movetoPatched}" ;;
        t)  titleSuffix="${OPTARG//[^\.\-\:\ \+_a-zA-Z0-9]/}"
            forceCmake="yes"
            printf "%s\n" "Titlebar version suffix: ${titleSuffix}" ;;
        u)  gcVer="$(curl "http://rawtherapee.googlecode.com/hg/tools/buildRT" 2>/dev/null | grep "^#.*[vV]ersion.*")" || { echo "\"curl\" program not found, please install it first."; exit 1; }
            gcVer="${gcVer##*[[:alpha:]] }"
            gcVer="${gcVer%%,*}"
            latestVer="$(printf "%s\n" "$version" "$gcVer" | sort -rV | head -n 1)"
            if [[ $version = $latestVer ]]; then
                printf "%s\n" "You are using the latest version of buildRT, $version"
                exit 0
            else
                printf "%s\n" "You are using version $version but version $gcVer is available on Google Code." "You can download the Google Code version from this URL:" "  https://rawtherapee.googlecode.com/hg/tools/buildRT" "Replace it with this script, and remember to run \"chmod +x buildRT\""
                exit 0
            fi ;;
        v)  verbose=yes
            printf "%s\n" "Verbose mode, I will spam your screen with warnings" ;;
        h|\?|-) printf "%s\n" "Usage:" "" "  $0 [-b] [-c <string>] [-f] [-n] [-p <1-9>] [-s <string>] [-t \"<string>\"] [-v]" ""
            printf "%s\n" \
            "  -b" \
            "Build-only mode. buildRT uses \"hg update -C default\" to update your source code repository to the newest revision, however doing so might destroy any uncommitted or unpushed changes you made or any patches you applied. With the -b flag the script will not update the source code, so that you can easily compile RawTherapee with whatever patches you manually applied. buildRT should automatically detect if you modified the source code, but you can use this flag to force build-only mode." "Generally when compiling patched RT versions you want to keep the cache and config folders separate, so consider using \"-b -c _testing\"" "" \
            "  -c <string>" \
            "Specify a suffix to the cache and config directory names. Only alphanumerics, periods, dashes and underscores are valid. The default value is \"4\", which will result in your build of RawTherapee storing the cache in \"${HOME}/.cache/RawTherapee4\" and config in \"${HOME}/.config/RawTherapee4\". For example, use \"-c _testing\" if you want to test older or patched versions of RawTherapee without potentially damaging your \"real\" cache and config files." "" \
            "  -f" \
            "Force CMake to re-run." "" \
            "  -n" \
            "Disable OpenMP." "" \
            "  -p <1-9>" \
            "Set which processor target to use. Takes a single digit from 1 to 9. The default is 2. See ProcessorTargets.cmake" "" \
            "  -s <string>" \
            "Suffix of destination build directory, so that if you have applied a patch, say \"dustremoval-1.patch\", and want to have RawTherapee compiled to a folder whose name ends with \"_dustremoval1\", you would set \"-s dustremoval1\" (the underscore is automated)." "" \
            "  -t \"<string>\"" \
            "Suffix displayed next to the RawTherapee version in the window titlebar. It is recommended that you include the changeset of the newest public commit (the one you would see if you cloned the repository anew) so it is clear which commit you applied the patches to. E.g.:" "-t \": ee72ddbcfd4e + dustremoval-1.patch + mustafa ibrahim\"" "" \
            "  -u" \
            "Check for an update of buildRT on Google Code." "" \
            "  -v" \
            "Make compilation verbose, so you see all compiler warnings." | fold -s
        exit 0 ;;
esac
done
shift $((OPTIND-1))
[ "$1" = "--" ] && shift

printf "%s\n" "Repository: ${repo}"
printf "%s\n" "Processor target: ${procTarget}"

if [[ -z $verbose ]]; then
  Wcflags="-Wno-unused-result -Wno-aggressive-loop-optimizations"
fi

cpuCount="$(grep -c 'processor' /proc/cpuinfo)"
# We can assume that if grep returns more than 32 lines (CPUs), or nothing at all, something's wrong
if (( cpuCount < 1 || cpuCount > 32 )); then
  cpuCount="1"
fi
printf "%s\n" "CPU count: ${cpuCount}"

# Zenity --notification is broken in <=3.8.0, removed Zenity support for now.
# elif hash zenity 2>/dev/null; then alert_type="zenity"
if hash notify-send 2>/dev/null; then alert_type="notify-send"
elif hash kdialog 2>/dev/null; then alert_type="kdialog"
elif hash xmessage 2>/dev/null; then alert_type="xmessage"
else alert_type="none"
fi

# list from http://linuxmafia.com/faq/Admin/release-files.html
distributions=(
"Annvix /etc/annvix-release"
"Arch /etc/arch-release"
"Arklinux /etc/arklinux-release"
"Aurox /etc/aurox-release"
"BlackCat /etc/blackcat-release"
"Cobalt /etc/cobalt-release"
"Conectiva /etc/conectiva-release"
"Debian /etc/debian_version"
"Fedora /etc/fedora-release"
"Gentoo /etc/gentoo-release"
"Immunix /etc/immunix-release"
"Knoppix knoppix_version"
"Linux-From-Scratch /etc/lfs-release"
"Linux-PPC /etc/linuxppc-release"
"Mandrake /etc/mandrake-release"
"Mandriva_Mandrake /etc/mandriva-release /etc/mandrake-release /etc/mandrakelinux-release"
"Mint /etc/linuxmint/info"
"MkLinux /etc/mklinux-release"
"Novell /etc/nld-release"
"PLD /etc/pld-release"
"RedHat /etc/redhat-release"
"CentOS /etc/centos-release"
"Slackware /etc/slackware-version"
"SME /etc/e-smith-release"
"Solaris /etc/release"
"SunJDS /etc/sun-release"
"SUSE /etc/SuSE-release"
"TinySofa /etc/tinysofa-release"
"TurboLinux /etc/turbolinux-release"
"Ubuntu /etc/lsb-release"
"UltraPenguin /etc/ultrapenguin-release"
"United /etc/UnitedLinux-release"
"VA-Linux /etc/va-release"
"YellowDog /etc/yellowdog-release"
)
for element in "${distributions[@]}"; do
  read distro loc1 loc2 loc3 <<< "$element"
  for loc in $loc1 $loc2 $loc3
  do
    # distribution=${distro} because if none of the elements match, distro will =YellowDog (last item in the list)
    # add "break 2;" to the end if we really want to, but Ubuntu gets detected as Debian first, then as Ubuntu,
    # so we might want to not break the loop.
    [[ -e "$loc" ]] && distribution=${distro}
    [[ "$distribution" = Gentoo ]] && break 2
  done
done
if [[ -z ${distribution} ]]; then
  printf "%s\n" "" "Could not automatically detect your distribution. Please enter your distribution's name below followed immediately by the version, without any spaces or punctuation marks, and hit enter to confirm, e.g. \"Ubuntu1310\", \"Mint15\" or \"OpenSUSE123\"" | fold -s
  read distribution
  #sanitize
  distribution=${distribution//[^a-zA-Z0-9]/}
fi
printf "%s\n" "Distribution: ${distribution}";

bits="$(uname -m)" || { printf "%s\n" "Is your system a 32-bit or 64-bit one?" "Enter 32 or 64 and hit enter: "; read bits; bits=${bits//[^0-9]/}; }
if [[ $bits = *64* ]]; then
  bits=64
else
  bits=32
fi
printf "%s\n" "System: ${bits}-bit" ""

#--- Check script dependencies
hash hg 2>/dev/null || { echo >&2 "Mercurial not found, install Mercurial first and then re-run this script."; exit 1; }

#--- Clone and/or pull
if [[ ! -d "${repo}" ]]; then
  printf "%s\n" "${repo} not found, cloning from GoogleCode..."
  hg clone https://rawtherapee.googlecode.com/hg/ "${repo}"
  cd "${repo}" || exit 1
  hg parents --template '\nRepository state:\n  Branch: {branch}\n  RawTherapee-{latesttag}.{latesttagdistance}\n  Changeset: {rev}:{node|short}\n  Latest tag: {latesttag}\n\n'
  alert "Repository cloned succesfully. What would you like to do next?"
  printf "%b" "Repository cloned succesfully.\n" "Press 'q' to quit or any other key to continue... "
  read -r -n 1
  echo
  [[ $REPLY = q || $REPLY = Q ]] && { printf "%s\n" "Quitting." ""; exit 0; }
fi
cd "${repo}" || exit 1

#--- Update or decide what to do if user edited the source code (e.g. by applying a patch)
# "hg outgoing" takes time, so skip if buildonly
if [[ -z $patched ]]; then
  uncommitted="$(hg status | sed "s/^/\t/")"
  unpushed="$(hg outgoing -q | sed "s/^/\t/" || echo "Could not check for unpushed changes (check your internet connection), but continuing anyway.")"
fi
if [[ -z $uncommitted && -z $unpushed && -z $patched ]]; then
  hg pull || echo "Could not \"hg pull\" (check your internet connection), but continuing anyway."
  hg update -C default
  echo
elif [[ -z $patched ]]; then
  printf "%s\n" "" "Warning! There are uncommitted or unpushed changes in the repository!" "Uncommitted:" "$uncommitted" "Unpushed:" "$unpushed" "" "This means that you edited the source code (e.g. applied a patch). If the script proceeds to update the repository, those changes you made to the source code might be lost. Your choices are to force the update and possibly lose the changes, not to update and to compile RT as-is, or to abort the script." | fold -s
  read -r -p "[f]orce update, [c]ompile as-is, or [a]bort? " fca
  case $fca in
    f|F) hg pull || echo "Could not \"hg pull\" (check your internet connection), but continuing anyway."
         hg update -C default
         echo ;;
    c|C) printf "%s\n" "Retaining edited source code and compiling RT as-is." ""
         patched="yes"
         if [[ -z $movetoPatched ]]; then
             movetoPatched="_patched"
         fi ;;
    *)   printf "%s\n" "User aborted" ""
         exit 0 ;;
  esac
else
  printf "%s\n" "Retaining edited source code and compiling RT as-is." ""
  if [[ -z $movetoPatched ]]; then
    movetoPatched="_patched"
  fi
fi
hg parents --template 'Repository state:\n  Branch: {branch}\n  RawTherapee-{latesttag}.{latesttagdistance}\n  Changeset: {rev}:{node|short}\n  Latest tag: {latesttag}\n\n'

#--- Print the menu
branches=()
if [[ -z $patched ]]; then
  while read -r branch; do
    branches+=("$branch")
  done < <(hg branches -aq | sort -f)
else
  branches="$(hg branch)"
fi

# Make the menu list
list=("0" "[abort]" "[exit]")
num="1"
buildTypes=("Release" "Debug")
for branch in "${branches[@]}"; do
  for buildType in "${buildTypes[@]}"; do
    list+=("$num" "${branch}" "${buildType}")
    ((num++))
  done
done

printf "%s\n" "---------------------------------------------"
printf "%s\t%s\t%s\n" "#" "Branch" "Build Type" "${list[@]}" | column -t -s $'\t' -c 3 | sed $'1s/.\+/\E[1m&\E[0m/'
printf "%s\n" "---------------------------------------------" "" "Enter your choices, each number separated by a single space, e.g. 3 4" "If you don't know which option to choose, then choose the \"default\" branch, \"Release\" build type." "" | fold -s

while [[ -z $choiceNumbers ]]; do
  read -r -p "Your choices: " -a choiceNumbers
done
printf "%s\n" "" "---------------------------------------------" ""
#sanitize
choiceNumbers="${choiceNumbers//[^0-9 ]/}"

#--- Compile the chosen builds
for choiceNumber in "${choiceNumbers[@]}"; do
  if [[ $choiceNumber = 0 ]]; then
    printf "%s\n" "User exited."
    exit 0;
  fi
  # ${array[@]:offset:length}
  # choiceNumber*3 to get the human menu choice to match the correct array index, and then +1 so we offset to branch and buildType, not #.
  IFS=$'\t' read -r branch buildType < <(printf "%s\t%s\n" "${list[@]:$(($((choiceNumber*3))+1)):2}")
  # This seems useless  "$branch != default"
  # if [[ -z $patched && $branch != default ]]; then
  if [[ -z $patched ]]; then
    printf "%s\n" "Updating to branch $branch"
    hg update -C "$branch" || exit 1
  fi
  echo
  printf "%-15b %b\n" "\E[1mWill compile\E[0m:" "" "\tChoice number:" "$choiceNumber" "\tBranch:" "$branch" "\tBuild type:" "$buildType" "\tTarget:" "$procTarget" ""
  rev="$(hg parents --template {rev})"

  [[ -d "${HOME}/rt_${branch}_${buildType}${movetoPatched}" ]] && {
  printf "%s\n" "Found old build directory ${HOME}/rt_${branch}_${buildType}${movetoPatched}" "To proceed you must either delete it, or choose a suffix for the destination folder for this build."
  read -r -p "[d]elete old build, [r]ename this build destination folder, or [a]bort "
  echo
  case $REPLY in
    d|D) rm -rf "${HOME}/rt_${branch}_${buildType}${movetoPatched}" || exit 1 ;;
    r|R) printf "%s\n" "The build will be saved to \"${HOME}/rt_${branch}_${buildType}_X\" where \"X\" will be replaced with whatever suffix you choose next. Only alphanumerics, dashes, underscores and periods are valid." | fold -s
         read -r -p "Suffix: "
         movetoPatched="_${REPLY//[^\.\-_a-zA-Z0-9]/}"
         printf "%s\n" "Build will be compiled to \"${HOME}/rt_${branch}_${buildType}${movetoPatched}\"" ;;
    a|A) printf "%s\n" "Cannot proceed if old build directory exists." "Remove it or rename it, then re-run this script." "Aborting"
         exit 0 ;;
      *) printf "%s\n" "Unknown response \"$REPLY\""
  esac
  }

  cd "${repo}" || exit 1

  verLatesttag="$(hg parents --template '{latesttag}')"
  verLatesttagdistance="$(hg parents --template '{latesttagdistance}')"
  [[ -z $dCacheNameSuffix ]] && dCacheNameSuffix="${verLatesttag%%.*}"

  # need to rerun cmake if buildtype changed
  if [[ -e build/CMakeCache.txt ]]; then
      previousBuildType="$(grep 'CMAKE_BUILD_TYPE:STRING=' build/CMakeCache.txt)"
      previousBuildType="${previousBuildType##*=}"
  fi
  if [[ ! -e build/CMakeCache.txt || $previousBuildType != "$buildType" ]]; then
      forceCmake="yes"
  fi

  if [[ ! -d "${repo}/build" || $forceCmake = yes ]]; then
      # Clean up leftovers from previous successful or failed builds
      [[ -d "${repo}/${buildType}" ]] && { printf "%s\n" "Found old build directory \"${repo}/$buildType\". Removing it."; rm -rf "${repo}/${buildType}"; }
      [[ -d "${repo}/rawtherapee" ]] && { printf "%s\n" "Found old build directory \"${repo}/rawtherapee\". Removing it."; rm -rf "${repo}/rawtherapee"; }
      [[ -d "${repo}/build" ]] && { printf "%s\n" "Found old build directory \"${repo}/build\". Removing it."; rm -rf "${repo}/build"; }
      printf "%s\n" "" "Cleaning out old CMake files"
      make clean || { printf "%s\n" "Error while running \"make clean\", aborting." "Easiest solution: delete ${repo} and re-run buildRT."; exit 1; }
      ./clean.sh || { printf "%s\n" "Error while running \"./clean.sh\", aborting." "Easiest solution: delete ${repo} and re-run buildRT."; exit 1; }
      mkdir "${repo}/build" || exit 1

      # As of changeset 1930:067e362c6f28 on Mon Jun 25 2012, revision number 1930, RT supports and encourages out-of-source builds.
      if (( rev < 1930 )); then
          cmake \
              -DCMAKE_BUILD_TYPE="$buildType" \
              -DPROC_TARGET_NUMBER="$procTarget" \
              -DCMAKE_C_FLAGS="-pipe" \
              -DCMAKE_CXX_FLAGS="$CMAKE_C_FLAGS $Wcflags" \
              "$noomp" \
              -DCMAKE_INSTALL_PREFIX="build" \
              -DBUILD_BUNDLE="ON" \
              -DBINDIR="." \
              -DDATADIR="." \
              -DCACHE_NAME_SUFFIX="$dCacheNameSuffix" \
              || { echo "Error during cmake, exiting."; exit 1; }
      else
          cd "${repo}/build"
          cmake \
              -DCMAKE_BUILD_TYPE="$buildType" \
              -DPROC_TARGET_NUMBER="$procTarget" \
              -DCMAKE_C_FLAGS="-pipe" \
              -DCMAKE_CXX_FLAGS="$CMAKE_C_FLAGS $Wcflags" \
              "$noomp" \
              -DCMAKE_INSTALL_PREFIX="build" \
              -DBUILD_BUNDLE="ON" \
              -DBINDIR="." \
              -DDATADIR="." \
              -DCACHE_NAME_SUFFIX="$dCacheNameSuffix" \
              -DVERSION_SUFFIX="$titleSuffix" \
              ../ \
              || { echo "Error during cmake, exiting."; exit 1; }
      fi
  fi
  echo

  if (( rev >= 1930 )); then
      cd "${repo}/build" || exit 1
  fi

  printf "%s\n" "" "Starting compilation:"
  time { make -j${cpuCount} install; } || { printf "%s\n" "" "Error during make, exiting."; exit 1; }
  printf "%-15b %b\n" "" "" "RawTherapee compiled:" "" "\tChoice number:" "$choiceNumber" "\tBranch:" "$branch" "\tBuild type:" "$buildType" "\tTarget:" "$procTarget" "\tCache:" "${HOME}/.cache/RawTherapee${dCacheNameSuffix}" "\tConfig:" "${HOME}/.config/RawTherapee${dCacheNameSuffix}" "" ""

  # RT used to build into various places over the years.
  # We want to end up with the build in a folder called "<repo>/build/rawtherapee" regardless of which old version you compile, and then to zip it, so we move dirs around:
  if (( rev < 1930 )); then
    if [[ -d "${repo}/${buildType}" ]]; then
      printf "%s\n" "Moving \"${repo}/${buildType}\" to \"${repo}/build/rawtherapee\""
      mv "${repo}/${buildType}" "${repo}/build/rawtherapee"
    elif [[ -d "${repo}/rawtherapee" ]]; then
      printf "%s\n" "Moving \"${repo}/rawtherapee\" to \"${repo}/build/rawtherapee\""
      mv "${repo}/rawtherapee" "${repo}/build/rawtherapee"
    elif [[ ! -d "${repo}/build" ]]; then
      { printf "%s\n" "Could not find the \"build\" directory containing the compiled RawTherapee in ${repo}" "Please notify DrSlony in the forum:" "http://rawtherapee.com/forum/viewtopic.php?f=10&t=3001#p22213" "" "Exiting"; exit 1; }
    fi
  elif [[ -d "${repo}/build/${buildType}" ]]; then
    printf "%s\n" "Moving \"${repo}/build/${buildType}\" to \"${repo}/build/rawtherapee\""
    mv "${repo}/build/${buildType}" "${repo}/build/rawtherapee"
  fi

  echo
  cd "${repo}/build"
  # ${repo}/build/AboutThisBuild.txt doesn't exist with older versions
  # Put "AboutThisBuild.txt" alongside the "rawtherapee" dir for the zip so that the website can extract the needed info when uploading the build (no other reason)
  if [[ ! -e AboutThisBuild.txt ]]; then
    cp "rawtherapee/AboutThisBuild.txt" AboutThisBuild.txt || { printf "%s\n" "Could not copy ${repo}/build/rawtherapee/AboutThisBuild.txt to ${repo}/build/AboutThisBuild.txt, exiting."; exit 1; }
  fi

  cat AboutThisBuild.txt || { printf "%s\n" "${repo}/build/AboutThisBuild.txt not found, exiting."; exit 1; }

  if [[ -z $patched ]]; then
    printf "%s\n" "Zipping the compiled RawTherapee dir \"${repo}/build/rawtherapee\" and putting it in \"/tmp/RawTherapee_${branch}_${distribution}_${bits}_${verLatesttag}.${verLatesttagdistance}_${buildType}.zip\""
    [[ -e "/tmp/RawTherapee_${branch}_${distribution}_${bits}_${verLatesttag}.${verLatesttagdistance}_${buildType}.zip" ]] && { rm "/tmp/RawTherapee_${branch}_${distribution}_${bits}_${verLatesttag}.${verLatesttagdistance}_${buildType}.zip" || exit 1; }
    zip -Xrq "/tmp/RawTherapee_${branch}_${distribution}_${bits}_${verLatesttag}.${verLatesttagdistance}_${buildType}.zip" AboutThisBuild.txt rawtherapee
  fi

  # Now that the zip is ready, the build can be moved to ~/rt_<branch>_<buildType><_patched>
  printf "%s\n" "" "Moving \"${repo}/build/rawtherapee\" to \"${HOME}/rt_${branch}_${buildType}${movetoPatched}\""
  mv "${repo}/build/rawtherapee" "${HOME}/rt_${branch}_${buildType}${movetoPatched}" || { printf "%s\n" "" "Could not move \"${repo}/build/rawtherapee\" to \"${HOME}/rt_${branch}_${buildType}${movetoPatched}\", exiting."; exit 1; }

  printf "%-15b %b\n" "" "" "Build ready:" "" "\tChoice number:" "$choiceNumber" "\tBranch:" "$branch" "\tBuild type:" "$buildType" "\tTarget:" "$procTarget"
  printf "%b\n" "" "\E[1mTo run RawTherapee\E[0m, fire up a terminal and type:" "~/rt_${branch}_${buildType}${movetoPatched}/rawtherapee" "" "------------------------------------------"
  alert "RawTherapee-${verLatesttag}.${verLatesttagdistance} ready.\nChoice number ${choiceNumber}, branch: ${branch}, type: ${buildType}, target: ${procTarget}"
done

# builds=( /tmp/RawTherapee* ); for f in ${builds[@]}; do echo ${f#/tmp/}; done
if [[ -z $patched ]]; then
  printf "%s\n" "RawTherapee zipped builds ready in /tmp"
  ls -lh /tmp/RawTherapee*
fi
printf "%s\n" "" "Finished building all chosen versions of RawTherapee"
