#!/bin/bash
# 
# Start docker environment for development.
#
# Usage:
#
#     fterm [options] [dbg|rel] [action] [arguments]
#
# For documentation run:
#
#     fterm --help
#
# For further tweaks see config/docker.template.cfg
#
# This is development fterm, simpler variants are available in the installation packages.
# See: https://github.com/flow123d/flow123d-package/tree/master/project/src/linux
#
#
#
# function will try to find proper dir to be mounted
# it will return $FLOW123D_WORK is set
# otherwise will return dir in $HOME, in which is flow123d located (can be even more nested)
# if the flow is not in a $HOME dir, will return parent directory
#    of the flow123d repo root direcotry (the one containing build-master etc)

set -x

# bin/fterm -- -di --name contrelease -euid=$(id -u) -v /opt/flow123d:/opt/flow123d

# get CWD as realtive path to current directory, which enables to build image on Windows
OLD_PWD="$(pwd)"
ABS_FLOW_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )"/.. && pwd )"
REL_FLOW_DIR=$(realpath --relative-to=$(pwd) $ABS_FLOW_DIR)

# define relative paths
#BIN="$REL_FLOW_DIR/bin"
CFG="$REL_FLOW_DIR/config"
#TMP="$REL_FLOW_DIR/config/docker/.tmp"
#DKR="$REL_FLOW_DIR/config/docker"


# default version of the image used when **running** docker image
image_version=dbg

# default tag of the used docker image: flow123d/flow-dev-gnu-${image_version}:${image_tag}
# can be specified explicitely from input:
# e.g. ./fterm @2.2.0 build
image_tag=latest

# In order to link the flow123d commit with particular image tag 
# see the file: config/build/image_tag
DOCKER_TAG="$CFG/build/image_tag"
if [[ -f "$DOCKER_TAG" ]]; then
  image_tag=$(cat "$DOCKER_TAG")
fi



function print_usage() {
  cat << EOF
  
Usage:  $bold fterm [dbg|rel] [tag] [options] [action] [arguments]$reset
  
Start docker container for Flow123d developing.
   
dbg       image with the debug build of the libraries (default), GNU environment by default
rel       image with optimized build of the libraries, GNU environment by default
dbg_gnu   image with the debug build of the libraries, GNU environment
rel_gnu   image with optimized build of the libraries, GNU environment
dbg_intel image with the debug build of the libraries, Intel environment
rel_intel image with optimized build of the libraries, Intel environment
vtune_gnu rel_gnu image with installed oneapi-basekit containing vtune


tag     version of the development image, e.g. @2.2.0, @3.0.0, @latest
        Images automatically downloaded from docker hub, see:  ${bblue} https://hub.docker.com/r/flow123d/flow-dev-gnu-dbg/tags ${reset}
        for avalilable tags. Default tag is taken form: ${bblue} config/docker/image_tag ${reset}
    
${bold}Options:${reset}
  ${bold}-h|--help ${reset}       fterm documentation
  ${bold}-V|--no-verbose ${reset} turn off configuration summary when starting container
  ${bold}-x|--set-x ${reset}      turn on script debuging mode (set -x)
  ${bold}-p|--privileged ${reset} add --privileged=true when starting docker container. SECURITY RISK
                   May be necessary on hosts with SELinux (mounting issues) or for using 
                   'gdb' within docker to turn off address space layout randomization. 
                   
  ${bold}-T|--no-term ${reset}    Turn off terminal for 'run' and 'exec' actions. Necessary when called from another process.

${bold}Actions:${reset}
  ${bold}shell${reset}            start interactive shell (default action)
  ${bold}exec${reset}             execute a linux command given by 'arguments' in the container
  ${bold}run${reset}              run flow123d binary with 'arguments'
    
                   These actions mount PWD and a work directory given by FLOW123D_WORK
                   or as a subdirectory of HOME containing the Flow123d root directory.
                   We prevent mounting full HOME as clashs with container's HOME.
    
  ${bold}--${reset}               execute 'docker run' with given image pass remaining arguments
  ${bold}update${reset}           update all flow123d images (download form docker hub)
  ${bold}clean${reset}            promptto remove unnamed images and containers

  ${bold}--detach <name> [<opts> ...]${reset}
                   start a new container <name> at background. 
                   ** Overwrites an container of the same name! **
    
${bold}Evironment variables:${reset}
  ${bold}nocolor${reset}          turn off terminal colors.
  ${bold}FLOW123D_WORK${reset}    explicit working directory to mount
  
${bold}Examples:${reset}
  ${bblue}bin/fterm dbg @2.2.0 exec make -C . all$reset  
  Start make of the Flow123d using the debug environment at version 2.2.0.
    
  ${bblue}bin/fterm rel run --help$reset
  Run flow123d --help command under the realease environment at default version.
EOF
#  ${bblue}bin/fterm dbg --detach dbg_run$reset
#      docker run <opts> ... flow123d/flow-dev-gnu-dbg:2.2.0

}

function dbg() {
  if [[ $verbose -eq 1 ]]; then
    echo -e "$bgreen[DBG]$reset $@"
  fi
}
function dbg2() {
  if [[ $verbose -eq 1 ]]; then
    echo -e "$byellow[DBG]$reset $@"
  fi
}
function dbgc() {
  if [[ $verbose -eq 1 ]]; then
    echo -e "$bblue[RUN]$reset $@"
  fi
  $@
}

function get_mount_dir() {
  if [[ -n $FLOW123D_WORK ]]; then
    echo $FLOW123D_WORK;
  else
    # try to find the first dir in $HOME, in which
    # flow123d is located
    local workdir=$1
    local workdir_tmp=
    for (( i = 0; i < 10; i++ )); do
      workdir_tmp=$(dirname $workdir)
      if [[ "$workdir_tmp" == "$HOME" ]]; then
        # we found what we were looking for
        echo $workdir
        return
      fi
      workdir=$workdir_tmp
    done
    # the folder is too deep or not in HOME
    # we return entire $HOME
    echo $(dirname $1)
  fi
}


# Will check whether given image exists and return 0 if it does
function image_exist() {
  did=$(docker images $1 -q)
  if [[ -z $did ]]; then
    return 1
  else
    return 0
  fi
}


# Will pull all the images to newest
function update_image() {
    pull_image=$1
    autopull=$2
    
    if [[ $autopull == "1" ]]; then
      docker pull $pull_image:$image_tag
    fi
}


# Detects exited containers  and prompt user to delete them
# Detects untagged images and prompt user to delete them
function remove_old() {
  if [ "$docker_terminal" == "" ]
  then
        YES_TO_ALL=1
  fi
  
  # ------------------------------------------------------------------------------
  EXITED=$(docker ps -q -f status=exited)
  if [[ -n $EXITED ]]; then
      echo "--------------------------------------------"
      echo "Note: You have exited containers present. Exited containers are usually"
      echo "      left overs which can be safely removed."
      echo "Containers are: "
      docker images | grep "^<none>"

      if [[ $YES_TO_ALL -eq 1 ]]; then
          docker rm -f $EXITED
      else
          read -p "Would you like to remove them [y/n]?" -r
          echo
          if [[ $REPLY =~ ^[Yy]$ ]]
          then
              docker rm -f $EXITED
          fi
      fi
      echo "--------------------------------------------"
  fi

  # ------------------------------------------------------------------------------
  UNNAMED=$(docker images | grep "^<none>" | awk "{print \$3}")
  if [[ -n $UNNAMED ]]; then
      echo "--------------------------------------------"
      echo "Note: You have untagged images present, this probably occured when images"
      echo "      were reinstalled. Even though these images can still be used it is "
      echo "      recommended to simply delete them."
      echo "Images are: "
      docker images | grep "^<none>"

      if [[ $YES_TO_ALL -eq 1 ]]; then
          docker rmi -f $UNNAMED
      else
          read -p "Would you like to remove them[y/n] ?" -r
          echo
          if [[ $REPLY =~ ^[Yy]$ ]]
          then
              docker rmi -f $UNNAMED
          fi
      fi
      echo "--------------------------------------------"
  fi
}

# check if stdout is a terminal...
if [[ -z "$nocolor" ]]; then
  if test -t 1; then
      # see if it supports colors...
      ncolors=$(tput colors)
      if test -n "$ncolors" && test $ncolors -ge 8; then
          bold="$(tput bold)"
          reset="$(tput sgr0)"
          red="$(tput setaf 1)"
          green="$(tput setaf 2)"
          yellow="$(tput setaf 3)"
          blue="$(tput setaf 4)"
          bblue="$bold$blue"
          bgreen="$bold$green"
          byellow="$bold$yellow"
          bred="$bold$red"
      fi
  fi
fi


# grab user's id
gid=$(id -g)
uid=$(id -u)

# not using $(whoami) so there are no collisions with $HOME
uname=flow
autopull=${autopull:-1}
theme=${theme:-light}

# source config, that way we know what to mount, etc.
DCFG="$CFG/docker.cfg"
if [[ -f "$DCFG" ]]; then
  source "$DCFG"
fi

# default settings
privileged=0
verbose=1
action=shell
work=$(get_mount_dir $ABS_FLOW_DIR)
contname=""
docker_terminal="-it"

manual_volumes=""

while [[ $# -gt 0 ]]
  do
  key="$1"
  case $key in
    -x|--set-x)
      set -x
      shift
    ;;
    -p|--privileged)
      privileged=1
      shift
    ;;
    -V|--no-verbose)
      verbose=0
      shift
    ;;
    --)
      shift
      action=raw
      rest="$@"
      break
    ;;
    --detach)
      contname="$2"
      action=detach
      shift; shift;
      rest="$@"
      break
    ;;
    -v)
      vol="$2"
      shift; shift;
      manual_volumes="$manual_volumes -v $vol"
      break
    ;;
    -T|--no-term)
      docker_terminal=""
      shift
    ;;
    clean|shell|run|exec|update)
      action=$1
      shift
      rest="$@"
      break
    ;;
    dbg|rel|dbg_gnu|rel_gnu|vtune_gnu|dbg_intel|rel_intel)
      image_version=$1
      shift
    ;;
    @*)
      image_tag=${1#@}  # cut at (@) from the value
      shift
    ;;
    build@*|update@*)
      echo "Please use new syntax to specify docker image version"
      echo "instead of: "
      echo "./fterm build@2.2.0"
      echo "use"
      echo "./fterm @2.2.0 build"
      exit 1
    ;;
    -h|--help)
      print_usage
      exit 1
    ;;
    -i|--images)
      shift
      flow123d_images=$1
      shift
    ;;
    *)
      echo -e "${bred}ERROR:$reset ${red} Invalid argument '$1'!$reset"
      print_usage
      echo -e "${bred}ERROR:$reset ${red} Invalid argument '$1'!$reset"
      exit 1
    ;;
  esac
done

image_kind=${image_version%%_*}   # rel / dbg
image_env=${image_version#*_}     # gnu /intel

if [ $image_kind = $image_env ]
then
  image_env="gnu"
fi

# set flow123d_images if empty
if [ -z "$flow123d_images" ]
then
flow123d_images="flow-dev-${image_env}-dbg flow-dev-${image_env}-rel"  # flow-dev-${image_env}-profile"
fi


# determine image names
flow_image=flow123d/flow-dev-$image_env-$image_kind:$image_tag


dbg "flow_image     = $flow_image"
dbg "image_tag      = $image_tag"
dbg "image_kind     = $image_kind"
dbg "image_env      = $image_env"
dbg "action         = $action"
dbg "mount dirs     - $work"
dbg "               - $OLD_PWD"
dbg "user           = $uname($uid:$gid)"
dbg "theme          = $theme"
dbg "autopull       = $autopull"
dbg "privileged     = $privileged"

# env variables which will be passed as well
envarg="-euid=$uid -egid=$gid -etheme=$theme -ewho=$uname -ehome=/mnt/$HOME -eFLOW123D_DIR=/$OLD_PWD"
if [ "${flow_image%:*}" == "flow123d/flow-dev-gnu-vtune" ]
then
  # port binding under Windows 10 needs host local IP adress to be specified.
  envarg="--name=vtune_cont --cap-add CAP_SYS_ADMIN --publish 127.0.0.1:7788:7788 ${envarg}"
fi

if [ -z "${manual_volumes}" ]
then
  mountargs="-w /${OLD_PWD} -v /${OLD_PWD}:/${OLD_PWD} -v /${work}:/${work}  -v /$HOME:/mnt/$HOME"
else
  mountargs="$manual_volumes"
fi

if [[ $privileged == "1" ]]; then
  priv_true="--privileged=true"
fi


# perform action based on variable action
case $action in
  raw)
    dbg2 "Raw fterm call: $rest"
    docker run $rest $flow_image
  ;;
  detach)
    docker rm -f $contname > /dev/null 2>&1
    dbgc docker run -d --name $contname ${docker_terminal} $envarg $mountargs $priv_true $rest $flow_image 
    #dbgc docker exec -u root $contname /usr/local/bin/entrypoint.sh id
  ;;
  run)
    update_image $flow_image $autopull
    dbg2 "Executing flow123d with args $rest"
    dbgc docker run --rm ${docker_terminal} $envarg $mountargs $priv_true $flow_image "$REL_FLOW_DIR/bin/flow123d" "$rest"
  ;;
  exec)
    update_image $flow_image $autopull
    dbg2 "Executing command $rest"
    dbgc docker run --rm ${docker_terminal} $envarg $mountargs $priv_true $flow_image "$rest"
  ;;
  shell)
    update_image $flow_image $autopull
    dbg2 "Entering shell"
    
    dbgc docker run --rm -it $envarg $mountargs $priv_true $flow_image
  ;;
  update)
    for image in $flow123d_images
    do
        update_image flow123d/$image 1
    done
  ;;
  clean)
    remove_old
  ;;
esac

exit $?
