#!/bin/bash

TV_SCRIPT_DIR="$(dirname "$(readlink -e "$0")")"
source "$TV_SCRIPT_DIR/tvw_main"

TV_MAJOR=15
TV_PACKAGE_INSTALL='yes'
TV_LOG_DIR="$TV_BASE_DIR/logfiles" # overriding default for TAR_NI

TV_DBUS_FILE_GUI='com.teamviewer.TeamViewer.service'
TV_DBUS_FILE_DESKTOP='com.teamviewer.TeamViewer.Desktop.service'
TV_DBUS_CONF_DAEMON='com.teamviewer.TeamViewer.Daemon.conf'
TV_POLKIT_FILE='com.teamviewer.TeamViewer.policy'

function TVSetup()
{
  CheckInteractive $#

  local -r mainAction="$1"
  local -r mainForce="$2"	# force
  local -r mainParam="$3"	# update / finish / redir (internal use)

  case "$mainAction" in
    ( 'checklibs' ) CheckLibs       ;;
    ( 'install'   ) InstallTar      ;;
    ( 'uninstall' ) InstallTar 'un' ;;
    ( *           ) PrintInfo       ;;
  esac
}

function CheckInteractive()
{
  [ "$1" = 0 ] || return	# prevent recursion in case of error

  cmdExists tty || return	# fail gracefully
  tty -s
  [ $? = 0 ] && return		# we have a tty - check successful

  # try to call self with terminal emulator
  local -r cmd="$0 --nocheck"
  local -r geomc='120x40+300+100'
  if cmdExists konsole; then
    konsole --hold -e "$cmd"
  elif cmdExists gnome-terminal; then
    gnome-terminal --geometry=$geomc -e "/bin/bash -c '$cmd; exec bash -i'"
  else
    xterm -geometry $geomc -hold -e "$cmd"
  fi

  exit
}

function CheckNoInstallation()
{
  [ "$TV_PKGTYPE" = 'TAR_NI' ]
}

function PrintInfo()
{
  echo
  ABecho 'How to use TeamViewer (tar.xz)'
  echo

  if CheckNoInstallation; then
    ABecho 'teamviewer' 'run teamviewer directly'
    echo   "   You can just extract the tar.xz package and run 'teamviewer' without installation."
    echo   '   It will behave similar to a TeamViewer Portable or QuickSupport on Windows.'
    echo   '   This should work if all necessary libraries (packages) are installed on your system.'
    echo
  fi

  ABecho 'tv-setup checklibs' 'identify missing libraries'
  echo   '   Run this command to identify missing libraries'
  echo   '   You can then look for the matching packages and install them manually.'
  echo
  ABecho 'tv-setup install' 'interactive installation'
  ABecho 'tv-setup install force' 'no questions'
  echo   '   A permanent installation with all the features of the RPM/DEB packages'
  echo   '   (start menu entries, auto start, background daemon for permanent access)'
  echo
  ABecho 'tv-setup uninstall [force]' 'undo previous (TAR) installation'
  echo   '   Removes the package. Log files and configuration are not removed'
  echo
  echo
}

function CheckSystemPackageMatch()
{
  checkSupportedArchitecture

  local -r testBin="$TV_BIN_DIR/TeamViewer"
  local -r sysArch="$(getSystemArchitecture)"
  local -r pkgArch="$(getFileArchitecture "$testBin")"

  [ "$sysArch" = "$pkgArch" ] && return

  Yecho "Your system  architecture appears to be '$sysArch', but this package is '$pkgArch'."

  if [ "$sysArch" == 'x86_64' ] && [ "$pkgArch" == 'x86_32' ]; then
    echo 'You may continue, but it is recommended to install the 64bit package'
  else
    Rdie 'Please get the appropriate package for your system.'
  fi
}

function CheckLibs()
{
  local -r silent="$1"

  if [ -n "$silent" ]; then
    ABecho 'Checking dependencies'
  else
    echo
    ABecho '   -=-   TeamViewer tar.xz check   -=-   '
    echo
    echo   '  In order to use the tar.xz version of TeamViewer, '
    echo   '  you have to make sure that the necessary libraries are installed.'
    echo
  fi

  cmdExists ldd || Rdie 'ldd not found. ldd needs to be installed'

  CheckSystemPackageMatch

  local logdir="$TV_LOG_DIR"									# save logs in TAR directory in interactive mode
  if [ -n "$silent" ] || (! isWritableDir "$logdir"); then		# save in /tmp in silent (install/root) mode (and always for Host)
    mkdir -p "$TV_CHECKLIB_DIR"
    logdir="$TV_CHECKLIB_DIR"
  fi

  local lddlog="$logdir/DependencyCheck.log"

  RunLddCheck "$lddlog" "$silent"

  local fail=
  AnalyzeLddResult "$lddlog" || fail=x;
  CheckQtQuick || fail=x
  [ -n "$fail" ] && LibInstallHint

  if [ -n "$silent" ]; then
    if [ -n "$fail" ]; then
      Recho "Serious Problem - installation should be aborted"
      AskPermission 'Missing libraries' 'TeamViewer will not be operational without these libraries. Please install them and try again.'
    fi

    # copy logfiles - make sure they will be included in a ziplog
    mkdir -p "$TV_TARIN_LOGDIR"
    cp $TV_CHECKLIB_DIR/* "$TV_TARIN_LOGDIR"
 fi

 echo
}

function isWritableDir()
{
  [ -d "$1" ] && [ -w "$1" ] && [ -x "$1" ]
}

function CheckQtQuick()
{
  if CheckQtQuickControls; then
    Gecho "\n\tQtQuickControls seems to be installed"
  else
    Recho "\n\tQtQuickControls seems to be missing"
    return 1
  fi
}

function CheckQtQuickControls()
{
  ls /usr/lib*/*/qt5/qml/QtQuick/Controls/qmldir &>/dev/null && return	# DEB
  ls /usr/lib*/qt5/qml/QtQuick/Controls/qmldir   &>/dev/null          	# RPM
}

function LibInstallHint()
{
  local -r debCmd=$(debCommand)
  local -r rpmCmd=$(rpmCommand)

  local -r debGeneral='libdbus-1-3'
  local -r debQtBase='libqt5gui5 libqt5widgets5 libqt5qml5 libqt5quick5 libqt5webkit5 libqt5x11extras5'
  local -r debQtModule='qml-module-qtquick2 qml-module-qtquick-controls qml-module-qtquick-dialogs qml-module-qtquick-window2 qml-module-qtquick-layouts '
  local -r debQtPlugin='qtdeclarative5-qtquick2-plugin qtdeclarative5-controls-plugin qtdeclarative5-dialogs-plugin qtdeclarative5-window-plugin qtdeclarative5-quicklayouts-plugin'
  local -r debVarA="$debGeneral $debQtBase $debQtModule"
  local -r debVarB="$debGeneral $debQtBase $debQtPlugin"

  local -r rpmGeneral='libdbus-1.so.3'
  local -r rpmQtBase='libQt5Gui.so.5 libQt5Widgets.so.5 libQt5Qml.so.5 libQt5Quick.so.5 libQt5WebKitWidgets.so.5 libQt5X11Extras.so.5'
  local -r rpmQtPlugin='qt5-qtdeclarative qt5-qtquickcontrols'
  local -r rpm32="$rpmGeneral $rpmQtBase $rpmQtPlugin"
  local -r rpm64="$(wordAppend "$rpmGeneral $rpmQtBase" "()(64bit)'" "'") $rpmQtPlugin"

  if hasDebSupport; then
    PrintLibInstallHint "$debCmd" "$debVarA" && return
    PrintLibInstallAlt  "$debCmd" "$debVarB" && return
  fi

  if hasRpmSupport; then
    if hasX86_64Support; then
      PrintLibInstallHint "$rpmCmd" "$rpm64" && return
    elif hasX86_32Support; then
      PrintLibInstallHint "$rpmCmd" "$rpm32" && return
    elif hasArmhfSupport; then
      PrintLibInstallHint "$rpmCmd" "$rpm32" && return
    else
      echo 'Unexpected architecture'
    fi
  fi
}

function PrintLibInstallHint()
{
  echo -e "\n\tThe following command may be helpful:\n\t  \E[1m$1 $2\E[0m\n"
}

function PrintLibInstallAlt()
{
  echo -e "\n\tAlternative command (different names):\n\t  \E[1m$1 $2\E[0m\n"
}

function hasDebSupport()
{
  cmdExists apt-get && return
  cmdExists dpkg
}

function hasRpmSupport()
{
  cmdExists zypper && return
  cmdExists dnf    && return
  cmdExists yum    && return
  cmdExists rpm
}

function debCommand()
{
  cmdExists apt-get && echo "apt-get install"
}

function rpmCommand()
{
  cmdExists zypper && echo "zypper install" && return
  cmdExists dnf    && echo "dnf install"    && return
  cmdExists yum    && echo "yum install"
}

function wordAppend()
{
  local -r list="$1"
  local -r appendix="$2"
  local -r prefix="$3"
  local result=
  local word=

  for word in $list; do
    result="$result$prefix$word$appendix "
  done

  echo "$result"
}

function InstallTar()
{
  local -r un="$1"

  InstallTarHead

  isSuperUser || Rdie "You need to be root to ${un}install the package"

  # only call once during installation. (Don't call on finish or un uninstall/update)
  [ -z "$mainParam" ] && [ -z "$un" ] && CheckLibs silent

  local -r installDir="$TV_INSTALL_BASE_DIR"
  local -r tvwConfig="$installDir/tv_bin/script/tvw_config"
  local -r instSetup="$installDir/tv_bin/script/teamviewer_setup"

  if [ -n "$un" ]; then     # uninstall
    PatchSetup
    UseInstalledUninstaller
    UnInstallTar
  else                      # install
    if [ "$mainParam" == 'finish' ]; then
      Configure
    else
      PrepareInstallation
      InstallFiles
      FinishInstallation
    fi
  fi
}

function InstallTarHead()
{
  if [ -z "$mainForce" ]; then

    case "$mainParam" in
      ( 'update' | 'redir' ) [ -n "$un" ] && return ;;	# don't show uninstall header twice (during update/redirected uninstall)
      ( 'finish'           ) [ -z "$un" ] && return ;;	# don't show install header during configure
    esac

    echo
    ABecho "   -=-   TeamViewer tar.xz interactive ${un}installation   -=-   "
    echo
  else
    [ "$mainForce" = "force" ] || Rdie "Invalid argument: '$mainForce'."
  fi
}

function UseInstalledUninstaller()
{
  isInstalledTV && return	# already installed installer: return and continue

  [ -x "$instSetup" ] || Rdie "Can't uninstall package: missing file '$instSetup'."
  "$instSetup" uninstall "$mainForce" redir || Rdie 'Failed to uninstall package'
  exit
}

function UnInstallTar()
{
  local -r uninstMsg='Uninstalling old package...'
  local uninstall

  UninstallListAdd "$installDir/tv_bin"      # must exist if tvwConfig exists
  UninstallListAdd "$installDir/doc"
  UninstallListAdd "$installDir/logfiles"
  UninstallListAdd "$installDir/config"
  UninstallListAdd "/usr/bin/teamviewer"
  UninstallListAdd "/usr/share/dbus-1/services/$TV_DBUS_FILE_GUI"
  UninstallListAdd "/usr/share/dbus-1/services/$TV_DBUS_FILE_DESKTOP"
  UninstallListAdd "/usr/share/dbus-1/system.d/$TV_DBUS_CONF_DAEMON"
  UninstallListAdd "/usr/share/polkit-1/actions/$TV_POLKIT_FILE"
# UninstallListAdd "/var/log/teamviewer$TV_MAJOR"	# don't remove, according to help message
# UninstallListAdd '/etc/teamviewer'

  CheckCurrentInstallationType

  [ -n "$mainForce" ] && ABecho "$uninstMsg" # print message, even if not asking for permission
  AskPermission "$uninstMsg" "These files and directories will be deleted:

    $(ls -gd --time-style=+ $uninstall)
    "

  MakeMenuEntries 'uninstall'
  removeDaemon
  echo "Removing files...$uninstall"

  rm -rf $uninstall || Rdie 'Could not delete files'

  if [ "$mainParam" == "update" ]; then
    echo
  else
    Gecho   '  Done!'
    echo -e '    TeamViewer TAR has been sucessfully removed.'
    echo
  fi
}

function UninstallListAdd()
{
  expectVariables uninstall

  local -r path="$1"
  [ -e "$path" ] || return
  [ -d "$path" ] && [[ "$path" != '/opt/'* ]] && return
  [ -n "$uninstall" ] && uninstall+=' '
  uninstall+="$path"
}

function PrepareInstallation()
{
  [ -d "$installDir" ] || return

  if [ -f "$tvwConfig" ]; then		# already installed
    CheckCurrentInstallationType
    PatchSetup
    [ -x "$instSetup" ] || Rdie "Can't uninstall previous package: missing file '$instSetup'."

    "$instSetup" uninstall "$mainForce" update || Rdie 'Failed to uninstall previous package'
  fi
}

function PatchSetup()
{
  (grep -q 'TV_MAJOR=12' $instSetup && grep -q '$TV_DBUS_FILE"' $instSetup && grep -q 'TV_DBUS_FILE_GUI' $instSetup) || return
  sed -i -e 's|$TV_DBUS_FILE"|$TV_DBUS_FILE_GUI"|g' $instSetup
}

function FinishInstallation()
{
    [ -x "$instSetup" ] || Rdie "Can't finish installation: missing file '$instSetup'."
    "$instSetup" install "$mainForce" finish
}

function Configure()
{
  InitGlobalSettings			# before installDaemon

  AskPermission 'Install daemon?' "Note: You can (un)install the daemon at any time.
  Commands are explained in 'teamviewer help'" 'return'
  [ $? = 0 ] && installDaemon

  AskPermission 'Create menu entries?' 'Creates menu entries for your desktop environment.' 'return'
  [ $? = 0 ] && MakeMenuEntries 'install'

  Gecho   '  Done!'
  echo -e '    TeamViewer TAR has been sucessfully installed.'
  echo -e '    Run \E[1mteamviewer help\E[0m for more information.'
  echo
}

function CheckCurrentInstallationType()
{
  [ -f "$tvwConfig" ] || die 'internal error'

  grep -q 'TV_PKGTYPE=.TAR_IN.' "$tvwConfig" && return	# previous TAR installation
  grep -q 'TV_PKGTYPE=.TAR_NI.' "$tvwConfig" && return	# previous TAR installation failed -> InstallFiles()
  Rdie "\tTeamViewer seems to be installed already in '$installDir', but with a different package type ($(source "$tvwConfig"; echo $TV_PKGTYPE)).
	  Please uninstall DEB/RPM package before trying to install from TAR package."
}

function InstallFiles()
{
  local -r binSrc="$TV_BIN_DIR"
  local -r docSrc="$TV_BASE_DIR/doc"
  local -r lnsDst="$installDir/tv_bin/script"

  AskPermission 'Installing files...' "Files will be installed in '$installDir'"

  mkdir -p "$installDir" || Rdie "could not create $installDir"

  ABecho 'Copying files...'
  RenameOldDirs
  DirLink "/var/log/teamviewer$TV_MAJOR" "$installDir/logfiles"      || Rdie 'fail (InstallFiles)'
  DirLink '/etc/teamviewer'              "$installDir/config"        || Rdie 'fail (InstallFiles)'

  ln -s "$lnsDst/teamviewer"      '/usr/bin/teamviewer'                         || Rdie 'fail (InstallFiles)'
  ln -s "$lnsDst/$TV_DBUS_FILE_GUI"     "/usr/share/dbus-1/services/$TV_DBUS_FILE_GUI"     # ignore errors - optional file
  ln -s "$lnsDst/$TV_DBUS_FILE_DESKTOP" "/usr/share/dbus-1/services/$TV_DBUS_FILE_DESKTOP" # ignore errors - optional file
  ln -s "$lnsDst/$TV_POLKIT_FILE"       "/usr/share/polkit-1/actions/$TV_POLKIT_FILE"      # ignore errors - optional file

  install --mode=644 "$binSrc/script/$TV_DBUS_CONF_DAEMON" "/usr/share/dbus-1/system.d/$TV_DBUS_CONF_DAEMON"

  cp -r "$binSrc" "$installDir" || Rdie 'fail (InstallFiles)'
  cp -r "$docSrc" "$installDir" || Rdie 'fail (InstallFiles)'

  echo -e '  done\n'

  sed -i 's/TAR_NI/TAR_IN/g' "$tvwConfig" || Rdie "Could not update '$tvwConfig'"
}

function MakeMenuEntries()
{
  local -r param="$1"
  local -r dtSrc="$TV_DESKTOP_FILE"
  local -r dtName="${dtSrc##*/}"
  local -r dtSrcPath="${dtSrc%/$dtName}"
  local -r dtDst="/usr/share/applications/$dtName"

  if [ "$param" == 'install' ]; then
    InstallIconFiles
    UpdateIconCache   # before installing desktop file
    cp -i "$dtSrc" "$dtDst"     || die 'MakeMenuEntries failed'
  else
    InstallIconFiles
    rm -f "$dtDst"   || die 'MakeMenuEntries failed'
    UpdateIconCache
  fi

  cmdExists update-menus            && update-menus
  cmdExists update-desktop-database && update-desktop-database
}

function InstallIconFiles()
{
  InstallIconFile 16
  InstallIconFile 20
  InstallIconFile 24
  InstallIconFile 32
  InstallIconFile 48
  InstallIconFile 256
}

function InstallIconFile()
{
  expectVariables dtSrcPath param

  local -r px="$1"
  local -r iconSource="teamviewer_${px}.png"
  local -r sysIconDir="/usr/share/icons/hicolor/${px}x${px}/apps"
  local -r sysIconPath="$sysIconDir/TeamViewer.png"

  if [ "$param" == 'install' ]; then
    mkdir -p $sysIconDir
    cp -i $dtSrcPath/$iconSource   $sysIconPath
  else
    [ -f $sysIconPath ] && rm -f $sysIconPath
  fi
}

function UpdateIconCache()
{
  cmdExists update-icon-caches      && update-icon-caches    /usr/share/icons/hicolor
  cmdExists gtk-update-icon-cache   && gtk-update-icon-cache /usr/share/icons/hicolor
}

function DirLink()
{
  local -r dir="$1"
  local -r lnk="$2"

  [ -h "$dir" ] && Rdie 'fail (DirLink)'
  [ -d "$dir" ] || mkdir -p "$dir" || Rdie 'fail (DirLink)'
  if [ -h "$lnk" ]; then
    rm "$lnk"       || Rdie 'fail (DirLink)'
  fi

  ln -s "$dir" "$lnk"
}

function RenameOldDirs()
{
  MoveDir    '/opt/teamviewer/config'   || Rdie 'fail (RenameOldDirs)'
  MoveDir    '/opt/teamviewer/logfiles' || Rdie 'fail (RenameOldDirs)'
  RemoveLink '/etc/teamviewer'          || Rdie 'fail (RenameOldDirs)'
}

function MoveDir()
{
  local -r name="$1"
  local -r newName="$name.old"
  local cntName="$newName"
  local cnt=0

  [ -h "$name" ] && return 0
  [ -d "$name" ] || return 0

  if [ -e "$newName" ]; then
    while [ -e "$cntName" ] ; do
      cntName="$newName$((++cnt))"
    done
    mv "$newName" "$cntName" || return 1
  fi

  mv "$name" "$newName" || return 1
}

function RemoveLink()
{
  local -r name="$1"
  [ -h "$name" ] || return 0

  rm "$name"
}

function AskPermission()
{
  local -r head="$1"
  local -r msg="$2"
  local -r return="$3"
  local msel=

  local               question='    Continue (y) or abort (n) ?  [Y/n]? '
  [ -n "$return" ] && question='    Continue (y) or skip (n) ?  [Y/n]? '

  [ -n "$mainForce" ] && return		# don't ask

  ABecho "$head"
  IndentEcho "$msg" "    "
  read -n 1 -p "$question" msel
  echo -en '\n'
  [ -z "$msel"   ] && return 0		# default = Y
  [ "$msel" = Y  ] && return 0
  [ "$msel" = y  ] && return 0
  [ -n "$return" ] && return 1
  echo
  exit 1	# action was not sucessful, inform calling process
}

TVSetup "$@"
