#!/bin/bash

PKG="RoonCommandLine"
TOP="/usr"
DESTDIR="${TOP}/local"
ROON="${DESTDIR}/Roon"
PKG_NAME="RoonCommandLine"
ROONETC=${ROON}/etc
ROON_INI=${ROONETC}/roon_api.ini
ROONCONF=${ROONETC}/pyroonconf
TMP_ROON_API="/tmp/_roon_api_ini_.save"

# User should not be root. Prompt to proceed if root user
iamroot=
SUDO="sudo -E"
if [ "${EUID}" ]
then
  [ ${EUID} -eq 0 ] && {
    iamroot=1
    SUDO=
  }
else
  uid=`id -u`
  [ ${uid} -eq 0 ] && {
    iamroot=1
    SUDO=
  }
fi

[ "$1" == "unattended" ] && {
  export ROON_UNATTENDED="unattended"
  shift
}

BOLD=$(tput bold 2>/dev/null)
NORMAL=$(tput sgr0 2>/dev/null)

if [ -x /opt/homebrew/bin/brew ]; then
  eval "$(/opt/homebrew/bin/brew shellenv)"
else
  [ -x /usr/local/bin/brew ] && eval "$(/usr/local/bin/brew shellenv)"
fi
export PATH=$PATH:/usr/local/bin

have_git=$(type -p git)
have_jq=$(type -p jq)
have_brew=$(type -p brew)
[ "${have_git}" ] || {
  [ "${have_brew}" ] && brew install git > /dev/null 2>&1
}
[ "${have_jq}" ] || {
  [ "${have_brew}" ] && brew install jq > /dev/null 2>&1
}

PYTHON=
python3_inst=$(type -p python3)
if [ "${python3_inst}" ]; then
  PYTHON="python3"
else
  python_inst=$(type -p python)
  if [ "${python_inst}" ]; then
    [[ "$(python --version)" =~ "Python 3" ]] && PYTHON="python"
  fi
fi
[ "${PYTHON}" ] || {
  echo "RoonCommandLine requires Python 3 and Pip"
  echo "Install Python 3 and restart this installation"
  echo "See https://docs.python-guide.org/starting/install3/osx/"
  echo "for step by step instructions to install Homebrew and Python 3"
  echo "If you already have Homebrew, install Python 3 with:"
  echo "brew install python"
  exit 1
}

[ -d ${ROON} ] || ${SUDO} mkdir -p ${ROON}
# Create the RoonCommandLine Python virtual environment
${SUDO} ${PYTHON} -m venv ${ROON}/venv >/dev/null 2>&1
# Use the RoonCommandLine Python virtual environment
[ -f ${ROON}/venv/bin/activate ] && source ${ROON}/venv/bin/activate
[[ ":$PATH:" == *":/usr/local/Roon/venv/bin:"* ]] || {
  export PATH=/usr/local/Roon/venv/bin:${PATH}
}
[ -x ${ROON}/venv/bin/python ] && export PYTHON=${ROON}/venv/bin/python
# Make sure we have pip installed
${SUDO} ${PYTHON} -m ensurepip --upgrade >/dev/null 2>&1
[ -x ${ROON}/venv/bin/pip ] || {
  PYOUT="/tmp/get-pip$$.py"
  PYURL="https://bootstrap.pypa.io/get-pip.py"
  have_curl=$(type -p curl)
  if [ "${have_curl}" ]; then
    curl -sSL -o ${PYOUT} ${PYURL}
  else
    have_wget=$(type -p wget)
    if [ "${have_wget}" ]; then
      wget -q -O ${PYOUT} ${PYURL}
    else
      echo "WARNING: Unable to locate curl or wget to download pip install script"
    fi
  fi
  if [ -f ${PYOUT} ]; then
    ${SUDO} ${PYTHON} ${PYOUT}
  else
    echo "WARNING: pip install script not found"
  fi
  rm -f ${PYOUT}
}
[ -x ${ROON}/venv/bin/pip ] || {
  echo "WARNING: ${ROON}/venv/bin/pip not found or not executable"
}

SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd)

[ -f "${SCRIPT_DIR}/VERSION" ] || {
  echo "$SCRIPT_DIR/VERSION does not exist. Exiting."
  exit 1
}

. "${SCRIPT_DIR}/VERSION"
version=${VERSION}
release=${RELEASE}

[ "${ROON_UNATTENDED}" ] || {
  while true; do
    read -r -p "Install ${PKG_NAME} version ${version}r${release} ? ('Y'/'N'): " yn
    case $yn in
      [Yy]*)
        break
        ;;
      [Nn]*)
        exit 0
        ;;
      *)
        echo "Please answer yes or no."
        ;;
    esac
  done
}

# Install/upgrade the Python Roon API
${SUDO} ${PYTHON} -m pip install --upgrade roonapi >/dev/null 2>&1
${SUDO} ${PYTHON} -m pip install rich-cli >/dev/null 2>&1

[ -d /usr/local ] || ${SUDO} mkdir /usr/local
[ -d /usr/local/bin ] || ${SUDO} mkdir /usr/local/bin
[ -d ${ROON} ] || ${SUDO} mkdir ${ROON}
[ -d ${DESTDIR}/share ] || ${SUDO} mkdir ${DESTDIR}/share
[ -d ${DESTDIR}/share/applications ] || ${SUDO} mkdir ${DESTDIR}/share/applications
[ -d ${DESTDIR}/share/doc ] || ${SUDO} mkdir ${DESTDIR}/share/doc
[ -d ${DESTDIR}/share/doc/${PKG} ] || ${SUDO} mkdir ${DESTDIR}/share/doc/${PKG}

[ -f ${ROON_INI} ] && {
  ${SUDO} cp ${ROON_INI} "${TMP_ROON_API}"
}
[ -f ${ROONCONF} ] && {
  ${SUDO} cp ${ROONCONF} /tmp/_pyroonconf_.save
}

for dir in bin etc api patches; do
  [ -d ${ROON}/${dir} ] && ${SUDO} rm -rf ${ROON}/${dir}
done

${SUDO} cp -a api ${ROON}/api
${SUDO} cp -a bin ${ROON}/bin
${SUDO} cp -a etc ${ROON}/etc
${SUDO} cp VERSION ${ROON}/etc/VERSION
${SUDO} cp -a patches ${ROON}/patches

${SUDO} cp ./*.desktop "${DESTDIR}/share/applications"
${SUDO} cp AUTHORS ${DESTDIR}/share/doc/${PKG}/AUTHORS
${SUDO} cp LICENSE ${DESTDIR}/share/doc/${PKG}/copyright
${SUDO} cp CHANGELOG.md ${DESTDIR}/share/doc/${PKG}/changelog
${SUDO} cp README.md ${DESTDIR}/share/doc/${PKG}/README
${SUDO} cp VERSION ${DESTDIR}/share/doc/${PKG}/VERSION
${SUDO} gzip -9 -f ${DESTDIR}/share/doc/${PKG}/changelog

${SUDO} chmod 644 man/*/*
${SUDO} chmod 755 man man/man*
for mandir in man/man*; do
  [ "${mandir}" == "man/man*" ] && continue
  [ -d ${DESTDIR}/share/"${mandir}" ] || {
    ${SUDO} mkdir -p ${DESTDIR}/share/"${mandir}"
  }
  ${SUDO} cp -p "${mandir}"/* ${DESTDIR}/share/"${mandir}"
done

[ -f .gitignore ] && {
  while read -r ignore; do
    ${SUDO} rm -f ${ROON}/"${ignore}"
  done <.gitignore
}

${SUDO} chmod 644 ${ROON}/etc/* \
  ${ROON}/api/* \
  ${ROON}/patches/*.md ${ROON}/patches/*.patch \
  ${ROON}/patches/roonapi/* \
  ${ROON}/patches/roon-web-controller/*
${SUDO} chmod 755 ${ROON}/bin/* \
  ${ROON}/bin \
  ${ROON}/etc/discover \
  ${ROON}/etc/install-fzf \
  ${ROON}/etc/install-roon-gui \
  ${ROON}/etc/install-roon-tui \
  ${ROON}/etc/install-figlet \
  ${ROON}/etc/install-gum \
  ${ROON}/etc/roon_faded \
  ${ROON}/etc \
  ${ROON}/api \
  ${ROON}/patches \
  ${ROON}/patches/mk* \
  ${ROON}/patches/roonapi \
  ${ROON}/patches/roon-web-controller
${SUDO} chown -R root:wheel ${ROON}
# ${SUDO} chown -R root:wheel ${DESTDIR}

# Try to configure the roon script with the IP and username
IP=$(ifconfig en0 | awk '/inet / {print $2; }')
ROON_SSH_USER=$(id -u -n)

cd /usr/local/bin || exit 1
for command in "${ROON}"/bin/*; do
  b=$(basename "${command}")
  if [ -f "${b}" ]; then
    diff "${command}" "${b}" >/dev/null || {
      ${SUDO} rm -f "${b}"
      ${SUDO} ln -s "${command}" .
    }
  else
    ${SUDO} ln -s "${command}" .
  fi
done

# Copy in distributed roon_api.ini template if no previous one exists
[ -s ${ROON_INI} ] || {
  # Restore previously saved roon_api.ini if one exists
  if [ -s "${TMP_ROON_API}" ]; then
    if grep defaultalbum "${TMP_ROON_API}" >/dev/null; then
      ${SUDO} cp ${ROONETC}/roon_api.ini-dist ${ROON_INI}
    else
      ${SUDO} cp "${TMP_ROON_API}" ${ROON_INI}
    fi
    ${SUDO} rm -f "${TMP_ROON_API}"
  else
    ${SUDO} cp ${ROONETC}/roon_api.ini-dist ${ROON_INI}
  fi
}
EX_CORE_IP=$(cat ${ROON_INI} | grep RoonCoreIP | awk -F '=' ' { print $2 } ')
EX_CORE_IP="$(echo -e "${EX_CORE_IP}" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')"
EX_CORE_PORT=$(cat ${ROON_INI} | grep RoonCorePort | awk -F '=' ' { print $2 } ')
EX_CORE_PORT="$(echo -e "${EX_CORE_PORT}" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')"

updatever=$(grep RoonCommandLineVersion ${ROON_INI})
if [ "${updatever}" ]; then
  cat ${ROON_INI} | sed -e "s/RoonCommandLineVersion.*/RoonCommandLineVersion = ${version}/" >/tmp/v$$
else
  cat ${ROON_INI} | sed -e "/RoonCorePort.*/a\\
RoonCommandLineVersion = ${version}" >/tmp/v$$
fi
${SUDO} cp /tmp/v$$ ${ROON_INI}
${SUDO} rm -f /tmp/v$$

updaterel=$(grep RoonCommandLineRelease ${ROON_INI})
if [ "${updaterel}" ]; then
  cat ${ROON_INI} | sed -e "s/RoonCommandLineRelease.*/RoonCommandLineRelease = ${release}/" >/tmp/r$$
else
  cat ${ROON_INI} | sed -e "/RoonCommandLineVersion.*/a\\
RoonCommandLineRelease = ${release}" >/tmp/r$$
fi
${SUDO} cp /tmp/r$$ ${ROON_INI}
${SUDO} rm -f /tmp/r$$

ROON_SSH_USER="root"
users=()
numusers=0
for homedir in /Users/*; do
  [ "${homedir}" == "/Users/*" ] && continue
  [ "${homedir}" == "/Users/Shared" ] && continue
  [ -d "${homedir}" ] && {
    username=$(basename "${homedir}")
    exists=$(id -g -n "${username}" 2>/dev/null)
    [ "${exists}" ] && {
      ROON_SSH_USER="${username}"
      users+=("${username}")
      numusers=$((numusers + 1))
    }
  }
done

[ $numusers -gt 1 ] && {
  # Check for ROON_USER environment variable, use if set
  if [ "${ROON_USER}" ] && [ -d /home/"${ROON_USER}" ]; then
      ROON_SSH_USER="${ROON_USER}"
      numusers=1
  else
    # Unattended installs use the last user found
    [ "${ROON_UNATTENDED}" ] && numusers=1
  fi
}
[ ${numusers} -gt 1 ] && {
  while true; do
    read -r -p "Using ${ROON_SSH_USER} as the Roon user. 'Y' for OK, 'N' to select a different user: " yn
    case $yn in
      [Nn]*)
        PS3="${BOLD}Please enter Roon user (numeric or text): ${NORMAL}"
        users+=("None")
        select opt in "${users[@]}"; do
          case "$opt,$REPLY" in
            "None",* | *,"None")
              break
              ;;
            *)
              [ -d /Users/${opt} ] && {
                ROON_SSH_USER="${opt}"
                break
              }
              printf "\nInvalid entry. Please try again"
              printf "\nEnter either a number or text of one of the menu entries\n"
              ;;
          esac
        done
        break
        ;;
      [Yy]*)
        break
        ;;
      *)
        echo "Please answer yes or no."
        ;;
    esac
  done
}

# Copy in saved pyroonconf if it exists
[ -s ${ROONCONF} ] || {
  # Restore previously saved pyroonconf
  if [ -s /tmp/_pyroonconf_.save ]; then
    echo "Restoring saved ${ROONCONF}"
    ${SUDO} cp /tmp/_pyroonconf_.save ${ROONCONF}
    ${SUDO} rm -f /tmp/_pyroonconf_.save
  fi
}

echo "Setting the Python Roon API server IP address to $IP"

echo "Enabling local access in ${ROONCONF}"
if [ -s ${ROONCONF} ]; then
  grep -v ^LOCAL= ${ROONCONF} >/tmp/roon$$
  echo "LOCAL=true" >>/tmp/roon$$
else
  echo "LOCAL=true" >/tmp/roon$$
fi
${SUDO} cp /tmp/roon$$ ${ROONCONF}
${SUDO} rm -f /tmp/roon$$

cat ${ROON}/bin/roon | sed -e "s/XX.X.X.XXX/$IP/" -e "s/SSH_USERNAME/$ROON_SSH_USER/" >/tmp/roon$$
${SUDO} cp ${ROON}/bin/roon ${ROON}/bin/roon.orig
${SUDO} cp /tmp/roon$$ ${ROON}/bin/roon
${SUDO} rm -f /tmp/roon$$

PYTHONUSERBASE=
# Check the global site directories
SITES=($(${PYTHON} -c 'import site; print(site.getsitepackages())' | tr -d '[],'))
for site in ${SITES[@]}; do
  site=$(echo $site | sed -e "s/'//g")
  site_sep=
  echo ${site} | grep site-packages >/dev/null && site_sep="site-packages"
  [ "${site_sep}" ] || site_sep="dist-packages"
  [ -d "${site}/roonapi" ] && {
    PYTHONUSERBASE=$(echo ${site} | awk -F "/lib/" ' { print $1 } ')
    PYTHON_SITEDIR=$(echo ${site} | awk -F "/${site_sep}" ' { print $1 } ')
    break
  }
done

if [ "${PYTHONUSERBASE}" ]; then
  if [ -s ${ROONCONF} ]; then
    grep PYTHONUSERBASE ${ROONCONF} >/dev/null || {
      echo "export PYTHONUSERBASE=${PYTHONUSERBASE}" | ${SUDO} tee -a ${ROONCONF} >/dev/null
    }
  else
    echo "export PYTHONUSERBASE=${PYTHONUSERBASE}" | ${SUDO} tee ${ROONCONF} >/dev/null
  fi
  . ${ROONCONF}

  RVER=$(${PYTHON} -m pip show roonapi | grep Version | awk -F ':' ' { print $2 } ')
  # Remove leading and trailing spaces in RVER
  RVER="$(echo -e "${RVER}" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')"
  # Only patch Roonapi version 0.0.38 and earlier
  LESSER=$(echo "${RVER} 0.0.38" | tr " " "\n" | sort -V | head -n 1)
  if [ "${LESSER}" != "${RVER}" ]; then
    # Mark it as having been patched
    grep -v ROONAPIPATCHED ${ROONCONF} >/tmp/roonconf$$
    echo "ROONAPIPATCHED=true" >>/tmp/roonconf$$
    ${SUDO} cp /tmp/roonconf$$ ${ROONCONF}
    ${SUDO} rm -f /tmp/roonconf$$
  else
    # Apply the Python Roon API patch if it has not already been applied
    if [ "${ROONAPIPATCHED}" = true ]; then
      echo "Python Roon API already patched. Skipping patch."
    else
      # Locate the patch file
      if [ "${site_sep}" == "site-packages" ]; then
        ROONAPI_PATCH=roonapi-listmedia-site.patch
      else
        ROONAPI_PATCH=roonapi-listmedia.patch
      fi
      patchfile=
      if [ -f ${ROON}/patches/${ROONAPI_PATCH} ]; then
        patchfile=${ROON}/patches/${ROONAPI_PATCH}
      else
        echo "Cannot locate patch file ${ROONAPI_PATCH}"
        echo "Python Roon API patch not applied."
        echo "List commands will not function properly."
      fi
      [ "${patchfile}" ] && {
        patch_inst=$(type -p patch)
        if [ "$patch_inst" ]; then
          cd "${PYTHON_SITEDIR}"
          ${SUDO} patch -b -p0 <${patchfile}
          grep -v ROONAPIPATCHED ${ROONCONF} >/tmp/roonconf$$
          echo "ROONAPIPATCHED=true" >>/tmp/roonconf$$
          ${SUDO} cp /tmp/roonconf$$ ${ROONCONF}
          ${SUDO} rm -f /tmp/roonconf$$
        else
          echo "Cannot locate the patch utility. Either patch is not installed"
          echo "or it is not in your execution PATH."
          echo ""
          echo "Skipping the patch for the Python Roon API."
          echo "Listing of Roon library media will not work without this patch."
        fi
      }
    fi
  fi
else
  echo "Could not locate the roonapi Python module installation directory"
  echo "Python Roon API patch not applied."
  echo "List commands will not function properly."
fi

[ "${ROON_SSH_USER}" ] && {
  GROUP=$(id -g -n "${ROON_SSH_USER}")
  ${SUDO} chown -R "${ROON_SSH_USER}":"${GROUP}" ${ROONETC}
}

# Install utilities used by the RoonCommandLine menu system
[ -x ${ROON}/etc/install-figlet ] && ${ROON}/etc/install-figlet
[ -x ${ROON}/etc/install-fzf ] && ${ROON}/etc/install-fzf
[ -x ${ROON}/etc/install-gum ] && ${ROON}/etc/install-gum

echo ""
if [ "${ROON_UNATTENDED}" ]; then
  echo ""
  echo "*************************** IMPORTANT ***************************"
  echo "Roon Core discovery and authorizing the RoonCommandLine extension"
  echo "must be performed post-installation in unattended installations."
  echo "After the installation of RoonCommandLine completes, run the command:"
  echo ""
  echo "    /usr/local/bin/roon -c discover"
  echo "*****************************************************************"
else
  ${ROON}/etc/discover "${SUDO}" "${SARG}" "${SUSR}"
fi
echo ""

[ "${ROON_SSH_USER}" ] && chown -R ${ROON_SSH_USER}:${GROUP} ${ROONETC}
