#!/bin/bash -e
#
# Copyright (c) 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
DOCKER_DIR="${ROOT}/../docker"

function usage() {
    print_info "Usage: build_base_image.sh" {target image, period delimited components, required} {target image name, optional} {disable_build boolean, optional}
    print_info "Copyright (c) 2022, NVIDIA CORPORATION."
}

DOCKER_SEARCH_DIRS=(${DOCKER_DIR})

# Read and parse config file if exists
#
# CONFIG_DOCKER_SEARCH_DIRS (array, can be empty)

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

    # Prepend configured docker search dirs
    if [ ${#CONFIG_DOCKER_SEARCH_DIRS[@]} -gt 0 ]; then
        for (( i=${#CONFIG_DOCKER_SEARCH_DIRS[@]}-1 ; i>=0 ; i-- )); do

            # If the path is relative, then prefix ROOT to the path
            if [[ "${CONFIG_DOCKER_SEARCH_DIRS[i]}" != /* ]]; then
                CONFIG_DOCKER_SEARCH_DIRS[$i]="${ROOT}/${CONFIG_DOCKER_SEARCH_DIRS[i]}"
            fi
        done

        CONFIG_DOCKER_SEARCH_DIRS+=(${DOCKER_SEARCH_DIRS[@]})
        DOCKER_SEARCH_DIRS=(${CONFIG_DOCKER_SEARCH_DIRS[@]})

        print_info "Using configured docker search paths: ${DOCKER_SEARCH_DIRS[*]}"
    fi
fi

TARGET_IMAGE_STR="$1"
if [[ -z "$TARGET_IMAGE_STR" ]]; then
    print_error "target image not specified"
    exit 1
fi
shift 1

TARGET_IMAGE_NAME="$1"
if [[ -z "$TARGET_IMAGE_NAME" ]]; then
    TARGET_IMAGE_NAME="${TARGET_IMAGE_STR//./-}-image"
    print_warning "Target image name not specified, using ${TARGET_IMAGE_NAME}"
fi
shift 1


BASE_IMAGE_NAME="$1"
if [[ -z "$BASE_IMAGE_NAME" ]]; then
    print_warning "Using base image name not specified, using '${BASE_IMAGE_NAME}'"
fi
shift 1

DOCKER_CONTEXT_DIR="$1"
if [[ -z "$DOCKER_CONTEXT_DIR" ]]; then
    print_warning "Using docker context dir not specified, using Dockerfile directory"
fi
shift 1

DOCKER_BUILDKIT=1
DISABLE_BUILDKIT_STR="$1"
if [[ ! -z "$DISABLE_BUILDKIT_STR" ]]; then
    print_warning "WARNING: Explicitly disabling BuildKit"
    DOCKER_BUILDKIT=0
fi
shift 1

ON_EXIT=()
function cleanup {
    for command in "${ON_EXIT[@]}"
    do
        $command &>/dev/null
    done
}
trap cleanup EXIT

PLATFORM="$(uname -m)"

# Resolve Dockerfiles by matching target image ids to available files
TARGET_IMAGE_IDS=(${TARGET_IMAGE_STR//./ })
IMAGE_IDS=(${TARGET_IMAGE_IDS[@]})

# Loop over components and find largest tail sequences
# For example, a target image id of 'aarch64.jp5.carter.nav' should match
# Dockerfiles with suffixes in the following order:
# ".aarch64.jp5.carter.nav", ".jp5.carter.nav", ".carter.nav", ".nav"
# If the first file found is ".carter.nav", the matching recurses by then
# looking for the preceding components ".aarch64.jp5" in the same manner

DOCKERFILES=()
DOCKERFILE_CONTEXT_DIRS=()
until [ ${#IMAGE_IDS[@]} -le 0 ]; do
    UNMATCHED_ID_COUNT=${#IMAGE_IDS[@]}

    for (( i=0; i<${#IMAGE_IDS[@]}; i++ )) do
        LAYER_IMAGE_IDS=${IMAGE_IDS[@]:i}
        LAYER_IMAGE_SUFFIX="${LAYER_IMAGE_IDS[@]// /.}"

        for DOCKER_SEARCH_DIR in ${DOCKER_SEARCH_DIRS[@]}; do
            DOCKERFILE="${DOCKER_SEARCH_DIR}/Dockerfile.${LAYER_IMAGE_SUFFIX}"

            if [[ -f "${DOCKERFILE}" ]]; then
                DOCKERFILES+=(${DOCKERFILE})
                DOCKERFILE_CONTEXT_DIRS+=(${DOCKER_SEARCH_DIR})
                IMAGE_IDS=(${IMAGE_IDS[@]:0:i})
                break 2
            fi
        done
    done

    if [ ${UNMATCHED_ID_COUNT} -eq ${#IMAGE_IDS[@]} ]; then
        UNMATCHED_IDS=${IMAGE_IDS[@]}
        MATCHED_DOCKERFILES=${DOCKERFILES[@]}
        print_error "Could not resolve Dockerfiles for target image ids: ${UNMATCHED_IDS// /.}"

        if [ ${#DOCKERFILES[@]} -gt 0 ]; then
            print_warning "Partially resolved the following Dockerfiles for target image: ${TARGET_IMAGE_STR}"
            for DOCKERFILE in ${DOCKERFILES[@]}; do
                print_warning "${DOCKERFILE}"
            done
        fi
        exit 1
    fi
done

# Arguments for docker build
BUILD_ARGS+=("--build-arg" "USERNAME="admin"")
BUILD_ARGS+=("--build-arg" "USER_UID=`id -u`")
BUILD_ARGS+=("--build-arg" "USER_GID=`id -g`")
BUILD_ARGS+=("--build-arg" "PLATFORM=$PLATFORM")

# Check if GPU is installed
if [[ $PLATFORM == "x86_64" ]]; then
    if type nvidia-smi &>/dev/null; then
        GPU_ATTACHED=(`nvidia-smi -a | grep "Attached GPUs"`)
        if [ ! -z $GPU_ATTACHED ]; then
            BUILD_ARGS+=("--build-arg" "HAS_GPU="true"")
        fi
    fi
fi

print_info "Resolved the following Dockerfiles for target image: ${TARGET_IMAGE_STR}"
for DOCKERFILE in ${DOCKERFILES[@]}; do
    print_info "${DOCKERFILE}"
done

# Build image layers
for (( i=${#DOCKERFILES[@]}-1 ; i>=0 ; i-- )); do
    DOCKERFILE=${DOCKERFILES[i]}
    DOCKERFILE_CONTEXT_DIR=${DOCKERFILE_CONTEXT_DIRS[i]}
    IMAGE_NAME=${DOCKERFILE#*"/Dockerfile."}
    IMAGE_NAME="${IMAGE_NAME//./-}-image"

    # Build the base images in layers first
    BASE_IMAGE_ARG=
    if [ $i -eq $(( ${#DOCKERFILES[@]} - 1 )) ]; then
        if [[ ! -z "${BASE_IMAGE_NAME}" ]] ; then
            BASE_IMAGE_ARG="--build-arg BASE_IMAGE="${BASE_IMAGE_NAME}""
        fi
    fi

    if [ $i -lt $(( ${#DOCKERFILES[@]} - 1 )) ]; then
        BASE_DOCKERFILE=${DOCKERFILES[i+1]}
        BASE_IMAGE_NAME=${BASE_DOCKERFILE#*"/Dockerfile."}
        BASE_IMAGE_NAME="${BASE_IMAGE_NAME//./-}-image"

        BASE_IMAGE_ARG="--build-arg BASE_IMAGE="${BASE_IMAGE_NAME}""
    fi

    # The last image should be the target image name
    # Use docker context dir script arg only for last image
    DOCKER_CONTEXT_ARG=${DOCKERFILE_CONTEXT_DIR}
    if [ $i -eq 0 ]; then
        IMAGE_NAME=${TARGET_IMAGE_NAME}
        if [[ ! -z "${DOCKER_CONTEXT_DIR}" ]]; then
            DOCKER_CONTEXT_ARG=${DOCKER_CONTEXT_DIR}
        fi
    fi

    print_warning "Building ${DOCKERFILE} as image: ${IMAGE_NAME} with base: ${BASE_IMAGE_NAME}"

    DOCKER_BUILDKIT=${DOCKER_BUILDKIT} docker build -f ${DOCKERFILE} \
     --network host \
     -t ${IMAGE_NAME} \
     ${BASE_IMAGE_ARG} \
     "${BUILD_ARGS[@]}" \
     $@ \
     ${DOCKER_CONTEXT_ARG}
done
