#!/bin/bash -e
#
# Copyright (c) 2021-2022, NVIDIA CORPORATION.  All rights reserved.
#
# NVIDIA CORPORATION and its licensors retain all intellectual property
# and proprietary rights in and to this software, related documentation
# and any modifications thereto.  Any use, reproduction, disclosure or
# distribution of this software and related documentation without an express
# license agreement from NVIDIA CORPORATION is strictly prohibited.

ROOT="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
source $ROOT/utils/print_color.sh

# Builds image (ARG MODE=deploy) with base image key, then layers any roots or debians you specify,
# then does rosdep install of a ROS WS if you specify one, then layers on suffix image key, and
# sets a default command to run 'ros2 launch {launch package} {launch file}'

# Example: installs two debians, copies three directories (includes ROS_WS/remaps one), runs package and launch file
# ./docker_deploy.sh -i "libnvvpi3,tensorrt" -d /workspaces/isaac_ros-dev/tests -d /home/nvidia/scripts:/home/admin/scripts -w /workspaces/isaac_ros-dev/ros_ws -b "aarch64.ros2_humble" -p "isaac_ros_image_proc" -f "isaac_ros_image_flip.launch.py"

# Read and parse config file if exists
#
# CONFIG_BASE_IMAGE_KEY (string, can be empty)

if [[ -f "${ROOT}/.isaac_ros_common-config" ]]; then
    . "${ROOT}/.isaac_ros_common-config"
fi

# Override with config from user home directory if exists
if [[ -f ~/.isaac_ros_common-config ]]; then
    . ~/.isaac_ros_common-config
fi

INCLUDE_DIRS=()
INCLUDE_TARBALLS=()
SET_LAUNCH_CMD=0
CUSTOM_APT_SOURCES=()
VALID_ARGS=$(getopt -o w:d:b:n:s:f:p:i:t:a: --long ros_ws:include_dir:base_image_key:name:suffix_image_key:launch_file:launch_package:install_debians:include_tarball:custom_apt_source -- "$@")
eval set -- "$VALID_ARGS"
while [ : ]; do
  case "$1" in
    -a | --custom_apt_source)
        CUSTOM_APT_SOURCES+=("$2")
        shift 2
        ;;
    -b | --base_image_key)
        BASE_IMAGE_KEY="$2"
        shift 2
        ;;
    -d | --include_dir)
        INCLUDE_DIRS+=("$2")
        shift 2
        ;;
    -f | --launch_file)
        LAUNCH_FILE="$2"
        shift 2
        ;;
    -i | --install_debians)
        INSTALL_DEBIANS_CSV="$2"
        shift 2
        ;;
    -n | --name)
        DEPLOY_IMAGE_NAME="$2"
        shift 2
        ;;
    -p | --launch_package)
        LAUNCH_PACKAGE="$2"
        shift 2
        ;;
    -s | --suffix_image_key)
        SUFFIX_IMAGE_KEY="$2"
        shift 2
        ;;
    -t | --include_tarball)
        INCLUDE_TARBALLS+=("$2")
        shift 2
        ;;
    -w | --ros_ws)
        ROS_WS="$2"
        shift 2
        ;;
    --) shift;
        break
        ;;
  esac
done

# Check arguments
PLATFORM="$(uname -m)"

# Check that both launch file and package are set, or neither
if [[ ! -z "${LAUNCH_FILE}${LAUNCH_PACKAGE}" ]]; then
    if [[ -z "${LAUNCH_FILE}" || -z "${LAUNCH_PACKAGE}" ]]; then
        print_error "Launch package (-p/--launch_package) and launch file (-f/--launch_file) must both be specified or both empty."
        exit 1
    fi
fi

if [[ ! -z "${LAUNCH_FILE}" ]]; then
    SET_LAUNCH_CMD=1
fi

if [[ -z "${BASE_IMAGE_KEY}" ]]; then
    BASE_IMAGE_KEY="${PLATFORM}.ros2_humble"
    print_warning "Base image key not specified, assuming $BASE_IMAGE_KEY"
fi

if [[ -z "${DEPLOY_IMAGE_NAME}" ]]; then
    DEPLOY_IMAGE_NAME="isaac_ros_deploy"
    print_warning "Deploy image name not specified, assuming $DEPLOY_IMAGE_NAME"
fi

if [[ ${#CUSTOM_APT_SOURCES[@]} -gt 100 ]]; then
    print_error "Unable to handle more than 100 custom apt sources."
    exit 1
fi

# Always include install directory of ROS workspace
if [[ ! -z "${ROS_WS}" ]]; then
    SYMLINKS_IN_INSTALL_SPACE=$(find "${ROS_WS}/install" -type l)
    if [ -n "${SYMLINKS_IN_INSTALL_SPACE}" ]; then
        print_warning "Found symlinks in install space. Symlinked install spaces are not supported. Please use an isolated or merge install instead. Symlinks:"
	    print_warning $SYMLINKS_IN_INSTALL_SPACE
    fi

    # Resolve ROS_WS_DEST from install directory setup.sh
    ROS_WS_DEST=/workspaces/isaac_ros-dev
    FILE_CONTENT=$(< "${ROS_WS}/install/setup.sh")
    REGEX="_colcon_prefix_chain_sh_COLCON_CURRENT_PREFIX=([^[:space:]]*)"
    if [[ $FILE_CONTENT =~ $REGEX ]]; then
        ROS_WS_DEST="${BASH_REMATCH[1]%/*}"
    fi

    INCLUDE_DIRS+=( "$ROS_WS/install:${ROS_WS_DEST}/install" )
fi

# Always suffix .user to base image
# If the configured key does not have .user, append it last
if [[ $BASE_IMAGE_KEY != *".user"* ]]; then
    BASE_IMAGE_KEY="${BASE_IMAGE_KEY}.user"
fi

# Summarize final arguments for script
print_info "Building deployable image ${DEPLOY_IMAGE_NAME}"
print_info "Base image key: |${BASE_IMAGE_KEY}| / suffix image_key: |${SUFFIX_IMAGE_KEY}|"
if [[ ! -z "${LAUNCH_FILE}" ]]; then
    print_info "Entrypoint to launch ${LAUNCH_PACKAGE}/${LAUNCH_FILE}"
fi
if [[ ! -z "${INSTALL_DEBIANS_CSV}" ]]; then
    print_info "Installing debians: ${INSTALL_DEBIANS_CSV}"
fi
if [[ ! -z "${ROS_WS}" ]]; then
    print_info "Installing ROS workspace at ${ROS_WS} to ${ROS_WS_DEST}"
fi
for INCLUDE_DIR in "${INCLUDE_DIRS[@]}"
do
    print_info "Installing directory: ${INCLUDE_DIR}"
done
for INCLUDE_TARBALL in "${INCLUDE_TARBALLS[@]}"
do
    print_info "Installing tarball: ${INCLUDE_TARBALL}"
done
for CUSTOM_APT_SOURCE in "${CUSTOM_APT_SOURCES[@]}"
do
    print_info "Adding custom apt source: ${CUSTOM_APT_SOURCE}"
done
print_info "Begin building deployable image"

# Setup on-exit cleanup tasks
ON_EXIT=()
function cleanup {
    for command in "${ON_EXIT[@]}"
    do
        $command
    done
}
trap cleanup EXIT

pushd . >/dev/null
cd $ROOT
ON_EXIT+=("popd")

# Setup staging temp directory
TEMP_DIR=`mktemp -d -t isaac_ros_deploy_XXXXXXXX`
ON_EXIT+=("rm -Rf ${TEMP_DIR}")

pushd . >/dev/null
ON_EXIT+=("popd")

cd $TEMP_DIR
cp -f $ROOT/deploy/_Dockerfile.deploy ${TEMP_DIR}/Dockerfile.deploy
cp -f $ROOT/deploy/_Dockerfile.deploy_ws ${TEMP_DIR}/Dockerfile.deploy_ws
cp -f $ROOT/deploy/_deploy-entrypoint.sh ${TEMP_DIR}/deploy-entrypoint.sh

mkdir -p ${TEMP_DIR}/staging

# Stage directories
for INCLUDE_DIR in "${INCLUDE_DIRS[@]}"
do
    SRC_DIR=${INCLUDE_DIR}
    DEST_DIR=${INCLUDE_DIR}
    INCLUDE_DIR_REMAP_ARRAY=(${INCLUDE_DIR//:/ })
    if [[ ${#INCLUDE_DIR_REMAP_ARRAY[@]} -gt 1 ]]; then
        SRC_DIR=${INCLUDE_DIR_REMAP_ARRAY[0]}
        DEST_DIR=${INCLUDE_DIR_REMAP_ARRAY[1]}
    fi

    print_info "Staging $SRC_DIR->$DEST_DIR"
    mkdir -p ${TEMP_DIR}/staging/${DEST_DIR#/}
    rsync -azL ${SRC_DIR}/ ${TEMP_DIR}/staging/${DEST_DIR#/}
done

# Stage tarballs
for INCLUDE_TARBALL in "${INCLUDE_TARBALLS[@]}"
do
    print_info "Staging tarball $INCLUDE_TARBALL"
    tar -xzvf $INCLUDE_TARBALL -C ${TEMP_DIR}/staging
done

# Delete all .git files and credentials
find $TEMP_DIR -type d -name ".git" | xargs -d '\n' rm -rf

BASE_DEPLOY_IMAGE_NAME="${DEPLOY_IMAGE_NAME}-base"
INSTALLED_DEPLOY_IMAGE_NAME="${DEPLOY_IMAGE_NAME}-installed"

# Stage custom apt sources
for (( i=0; i<${#CUSTOM_APT_SOURCES[@]}; i++ )) do
    printf -v APT_SOURCE_IDX "%02d" $i
    CUSTOM_APT_SOURCE_FILE="${TEMP_DIR}/staging/etc/apt/sources.list.d/${APT_SOURCE_IDX}_custom_apt_source"
    mkdir -p ${CUSTOM_APT_SOURCE_FILE%/*}
    echo "${CUSTOM_APT_SOURCES[$i]}" >> ${CUSTOM_APT_SOURCE_FILE}
done

# Build base image
print_info "Building deploy base image: ${BASE_DEPLOY_IMAGE_NAME} with key ${BASE_IMAGE_KEY}"
$ROOT/build_image_layers.sh --image_key "${BASE_IMAGE_KEY}" --image_name "${BASE_DEPLOY_IMAGE_NAME}" --build_arg "MODE=deploy"

# Install staged files and setup launch command
print_info "Building install image with launch file ${LAUNCH_FILE} in package ${LAUNCH_PACKAGE}"
$ROOT/build_image_layers.sh --image_key "deploy" --image_name "${INSTALLED_DEPLOY_IMAGE_NAME}" --base_image "${BASE_DEPLOY_IMAGE_NAME}" --context_dir "${TEMP_DIR}" \
    --build_arg "MODE=deploy" --build_arg "SET_LAUNCH_CMD=${SET_LAUNCH_CMD}" --build_arg "LAUNCH_FILE=${LAUNCH_FILE}" --build_arg "LAUNCH_PACKAGE=${LAUNCH_PACKAGE}" --build_arg "INSTALL_DEBIANS_CSV=${INSTALL_DEBIANS_CSV}"

# Optional, if ROS_WS, install rosdeps
if [[ ! -z "${ROS_WS}" ]]; then
    print_info "Building ROS workspace image for path ${ROS_WS}"
    PREVIOUS_STAGE="${INSTALLED_DEPLOY_IMAGE_NAME}"
    INSTALLED_DEPLOY_IMAGE_NAME="${DEPLOY_IMAGE_NAME}-rosws"
    $ROOT/build_image_layers.sh --image_key "deploy_ws" --image_name "${INSTALLED_DEPLOY_IMAGE_NAME}" --base_image "${PREVIOUS_STAGE}" --context_dir "${TEMP_DIR}" --build_arg "MODE=deploy ROS_WS=${ROS_WS_DEST}"
fi

# Optional, build suffix image if specified
if [[ ! -z "${SUFFIX_IMAGE_KEY}" ]]; then
    print_info "Building suffix deploy image for key ${SUFFIX_IMAGE_KEY}"
    PREVIOUS_STAGE="${INSTALLED_DEPLOY_IMAGE_NAME}"
    INSTALLED_DEPLOY_IMAGE_NAME="${DEPLOY_IMAGE_NAME}-suffix"
    $ROOT/build_image_layers.sh --image_key "SUFFIX_IMAGE_KEY" --image_name "${INSTALLED_DEPLOY_IMAGE_NAME}" --base_image "${PREVIOUS_STAGE}" --build_arg "MODE=deploy"
fi

# Retag last image
docker tag "${INSTALLED_DEPLOY_IMAGE_NAME}" "${DEPLOY_IMAGE_NAME}"
print_info "DONE, image: ${DEPLOY_IMAGE_NAME}"
