#!/bin/bash
#
# vi: expandtab tabstop=4 shiftwidth=0

set -o errexit
set -o pipefail

if [ "$DEBUG" == "true" ]; then
    set -ex
    export PS4='+(${BASH_SOURCE}:${LINENO}): ${FUNCNAME[0]:+${FUNCNAME[0]}(): }'
fi

readlink_mac() {
    cd $(dirname $1)
    TARGET_FILE=$(basename $1)

    # Iterate down a (possible) chain of symlinks
    while [ -L "$TARGET_FILE" ]; do
        TARGET_FILE=$(readlink $TARGET_FILE)
        cd $(dirname $TARGET_FILE)
        TARGET_FILE=$(basename $TARGET_FILE)
    done

    # Compute the canonicalized name by finding the physical path
    # for the directory we're in and appending the target file.
    PHYS_DIR=$(pwd -P)
    REAL_PATH=$PHYS_DIR/$TARGET_FILE
}

get_current_arch() {
    local current_arch
    case $(uname -m) in
    x86_64)
        current_arch=amd64
        ;;
    aarch64)
        current_arch=arm64
        ;;
    riscv64)
        current_arch=riscv64
        ;;
    esac
    echo $current_arch
}

ALLARCH=("amd64" "arm64" "riscv64")

pushd $(
    cd "$(dirname "$0")"
    pwd
) >/dev/null
readlink_mac $(basename "$0")
cd "$(dirname "$REAL_PATH")"
CUR_DIR=$(pwd)
SRC_DIR=$(cd .. && pwd)
popd >/dev/null

DOCKER_DIR="$SRC_DIR/build/docker"

# https://docs.docker.com/develop/develop-images/build_enhancements/
export DOCKER_BUILDKIT=1
# https://github.com/docker/buildx#with-buildx-or-docker-1903
export DOCKER_CLI_EXPERIMENTAL=enabled

REGISTRY=${REGISTRY:-docker.io/yunion}
TAG=${TAG:-latest}
CURRENT_ARCH=$(get_current_arch)
ARCH=${ARCH:-$CURRENT_ARCH}

build_bin() {
    local BUILD_ARCH=$2
    local BUILD_CGO=$3
    case "$1" in
    climc)
        rm -vf _output/bin/*cli
        env $BUILD_ARCH $BUILD_CGO make -C "$SRC_DIR" docker-alpine-build F="cmd/$1 cmd/*cli"
        ;;
    *)
        env $BUILD_ARCH $BUILD_CGO make -C "$SRC_DIR" docker-alpine-build F="cmd/$1"
        ;;
    esac
}

# build_bundle_libraries() {
#     for bundle_component in 'host-image'; do
#         if [ $1 == $bundle_component ]; then
#             $CUR_DIR/bundle_libraries.sh _output/bin/bundles/$1 _output/bin/$1
#             break
#         fi
#     done
# }

build_image() {
    local tag=$1
    local file=$2
    local path=$3
    local arch
    if [[ "$tag" == *:5000/* ]]; then
        arch=$(arch)
        case $arch in
        x86_64)
            docker buildx build -t "$tag" -f "$2" "$3" --output type=docker --platform linux/amd64
            ;;
        aarch64)
            docker buildx build -t "$tag" -f "$2" "$3" --output type=docker --platform linux/arm64
            ;;
        riscv64)
            docker buildx build -t "$tag" -f "$2" "$3" --output type=docker --platform linux/riscv64
            ;;
        *)
            echo wrong arch
            exit 1
            ;;
        esac
    else
        if [[ "$tag" == *"amd64" || "$ARCH" == "" || "$ARCH" == "amd64" || "$ARCH" == "x86_64" || "$ARCH" == "x86" ]]; then
            docker buildx build -t "$tag" -f "$file" "$path" --push --platform linux/amd64
        elif [[ "$tag" == *"arm64" || "$ARCH" == "arm64" ]]; then
            docker buildx build -t "$tag" -f "$file" "$path" --push --platform linux/arm64
        elif [[ "$tag" == *"riscv64" || "$ARCH" == "riscv64" ]]; then
            docker buildx build -t "$tag" -f "$file" "$path" --push --platform linux/riscv64
        else
            docker buildx build -t "$tag" -f "$file" "$path" --push
        fi
        docker pull "$tag"
    fi
}

buildx_and_push() {
    local tag=$1
    local file=$2
    local path=$3
    local arch=$4
    if [[ "$DRY_RUN" == "true" ]]; then
        echo "[$(readlink -f ${BASH_SOURCE}):${LINENO} ${FUNCNAME[0]}] return for DRY_RUN"
        return
    fi
    docker buildx build -t "$tag" --platform "linux/$arch" -f "$2" "$3" --push
    docker pull --platform "linux/$arch" "$tag"
}


get_image_name() {
    local component=$1
    local arch=$2
    local is_all_arch=$3
    local img_name="$REGISTRY/$component:$TAG"
    if [[ -n "$arch" && "$is_all_arch" == "true" ]]; then
        img_name="${img_name}-$arch"
    fi
    echo $img_name
}

build_process() {
    local component=$1
    local arch=$2
    local is_all_arch=$3
    local img_name=$(get_image_name $component $arch $is_all_arch)
    local build_env=""

    case "$component" in
    host)
        build_env="$build_env CGO_ENABLED=1"
        ;;
    *)
        build_env="$build_env CGO_ENABLED=0"
        ;;
    esac

    build_bin $component
    if [[ "$DRY_RUN" == "true" ]]; then
        echo "[$(readlink -f ${BASH_SOURCE}):${LINENO} ${FUNCNAME[0]}] return for DRY_RUN"
        return
    fi
    # build_bundle_libraries $component

    build_image $img_name $DOCKER_DIR/Dockerfile.$component $SRC_DIR
}

build_process_with_buildx() {
    local component=$1
    local arch=$2
    local is_all_arch=$3
    local img_name=$(get_image_name $component $arch $is_all_arch)

    build_env="GOARCH=$arch"
    case "$component" in
    host)
        build_env="$build_env CGO_ENABLED=1"
        ;;
    *)
        build_env="$build_env CGO_ENABLED=0"
        ;;
    esac

    case "$component" in
    host | torrent)
        buildx_and_push $img_name $DOCKER_DIR/multi-arch/Dockerfile.$component $SRC_DIR $arch
        ;;
    *)
        build_bin $component $build_env
        buildx_and_push $img_name $DOCKER_DIR/Dockerfile.$component $SRC_DIR $arch
        ;;
    esac
}

general_build() {
    local component=$1
    # 如果未指定，则默认使用当前架构
    local arch=${2:-$CURRENT_ARCH}
    local is_all_arch=$3

    if [[ "$CURRENT_ARCH" == "$arch" ]]; then
        build_process $component $arch $is_all_arch
    else
        build_process_with_buildx $component $arch $is_all_arch
    fi
}

make_manifest_image() {
    local component=$1
    local arch=$2
    local img_name=$(get_image_name $component "" "false")
    if [[ "$DRY_RUN" == "true" ]]; then
        echo "[$(readlink -f ${BASH_SOURCE}):${LINENO} ${FUNCNAME[0]}] return for DRY_RUN"
        return
    fi
    CMD="docker buildx imagetools create -t ${img_name} "
    for ac in "${ALLARCH[@]}"; do
        if [[ "${arch}" == "all" || "${arch}" == *"$ac"* ]]; then
            CMD="${CMD} ${img_name}-${ac}"
        fi
    done
    echo "$CMD"
    $CMD
}

ALL_COMPONENTS=$(ls cmd | grep -v '.*cli$' | xargs)

if [ "$#" -lt 1 ]; then
    echo "No component is specified~"
    echo "You can specify a component in [$ALL_COMPONENTS]"
    echo "If you want to build all components, specify the component to: all."
    exit
elif [ "$#" -eq 1 ] && [ "$1" == "all" ]; then
    echo "Build all onecloud docker images"
    COMPONENTS=$ALL_COMPONENTS
else
    COMPONENTS=$@
fi

cd $SRC_DIR
mkdir -p $SRC_DIR/_output

show_update_cmd() {
    local component=$1
    local spec=$1
    local name=$1
    local tag=$TAG

    case "$component" in
    'apigateway')
        spec='apiGateway'
        ;;
    'apimap')
        spec='apiMap'
        ;;
    'baremetal-agent')
        spec='baremetalagent'
        name='baremetal-agent'
        ;;
    'host')
        spec='hostagent'
        ;;
    'host-deployer')
        spec='hostdeployer'
        ;;
    'host-health')
        spec='hostagent/HostHealth'
        ;;
    'host-image')
        spec='hostimage'
        ;;
    'region')
        spec='regionServer'
        ;;
    'region-dns')
        spec='regionDNS'
        ;;
    'vpcagent')
        spec='vpcAgent'
        ;;
    'esxi-agent')
        spec='esxiagent'
        ;;
    'mcp-server')
        spec='mcpServer'
        ;;
    esac

    echo "kubectl patch oc -n onecloud default --type='json' -p='[{op: replace, path: /spec/${spec}/imageName, value: ${name}},{"op": "replace", "path": "/spec/${spec}/repository", "value": "${REGISTRY}"},{"op": "add", "path": "/spec/${spec}/tag", "value": "${tag}"}]'"
}

for component in $COMPONENTS; do
    if [[ $component == *cli ]]; then
        echo "Please build image for climc"
        continue
    fi

    echo "Start to build component: $component"

    multiarch=""
    for ac in "${ALLARCH[@]}"; do
        if [[ "$ARCH" == "$ac" ]]; then
            # single arch
            if [ -e "$DOCKER_DIR/Dockerfile.$component" ]; then
                general_build $component $ARCH "false"
            fi
        elif [[ "$ARCH" == "all" || "$ARCH" == *"$ac"* ]]; then
            multiarch="true"
            general_build $component $ac "true"
        fi
    done
    if [[ "$multiarch" == "true" ]]; then
        make_manifest_image $component $ARCH
    fi
done

echo ""
for component in $COMPONENTS; do
    if [[ $component == *cli ]]; then
        continue
    fi
    show_update_cmd $component
done
