#!/usr/bin/env bash

# Copyright 2021 The KubeEdge Authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

set -o errexit
set -o nounset
set -o pipefail

sedna::buildx::prepare_env() {
  # Check whether buildx exists.
  if ! docker buildx >/dev/null 2>&1; then
    echo "ERROR: docker buildx not available. Docker 19.03 or higher is required with experimental features enabled" >&2
    exit 1
  fi

  # Use tonistiigi/binfmt that is to enable an execution of different multi-architecture containers
  docker run --privileged --rm tonistiigi/binfmt --install all

  # Create a new builder which gives access to the new multi-architecture features.
  builder_instance="sedna-buildx"
  if ! docker buildx inspect $builder_instance >/dev/null 2>&1; then
    docker buildx create --use --name $builder_instance --driver docker-container
  fi
  docker buildx use $builder_instance

  # go speed tag with CGO_ENABLED=1 and alpine image
  _speed_buildx_for_cgo_alpine_
}

_speed_buildx_for_go_() {
  # docker speed build command for go
  echo 'go env -w GOOS="$TARGETOS" GOARCH="$TARGETARCH"' | _feed_to_dockerfile_RUN_command
}

_speed_buildx_for_cgo_alpine_() {
  # docker speed build command for go with CGO_ENABLED=1 and alpine image

  cat <<'EOF' | _feed_to_dockerfile_RUN_command
    # download cc(cross compiler) from https://musl.cc
    # the example url: https://musl.cc/aarch64-linux-musl-cross.tgz
    apk add curl

    # translate some architecture names
    arch=$(echo $TARGETARCH | sed "s@arm64@aarch64@")

    cc_url=$(curl musl.cc | grep /${arch}-${TARGETOS}-.*-cross)
    cc_filename=$(basename $cc_url)  # aarch64-linux-musl-cross.tgz
    cc_name=$(echo $cc_filename | cut -f1 -d.)  # aarch64-linux-musl-cross
    # download and extract
    curl -O $cc_url; tar xf $cc_filename

    # find the real cc name
    cc_bin=$PWD/$cc_name/bin  # aarch64-linux-musl-cross/bin
    export PATH="$cc_bin:$PATH"

    # cc=aarch64-linux-musl-cc
    # set CC path, and enable CGO
    go env -w CC=$(echo $cc_bin/$arch-$TARGETOS-*-cc) CGO_ENABLED=1

    go env -w GOOS="$TARGETOS" GOARCH="$TARGETARCH"
EOF
}

_feed_to_dockerfile_RUN_command() {
  # add '; \' for each command line
  speed_switch_cmd='RUN test "$TARGETPLATFORM" = "$BUILDPLATFORM" || ('
  printf "$speed_switch_cmd"
  while read run_cmd; do
    run_cmd=$(echo "$run_cmd" | sed 's@^\s*@@;s@\s*$@@;')   # skip leading/trailing spaces
    run_cmd="${run_cmd%# *}"  # skip comment
    run_cmd="${run_cmd%#;}"  # skip trailing ';'

    # skip empty command
    [ -n "${run_cmd}" ] && {
      echo "$run_cmd; \\\\"
    }
  done
  printf ")"
}

sedna::buildx:generate-speed-dockerfile() {
  # Add buildx improvement for cross compilation if Dockerfile is added by the specified tag
  # see more details for buildx https://github.com/docker/buildx#building-multi-platform-images
  local input_dockerfile=${1}

  local -a speed_tags=(
    # The cross buildx tag in Dockerfile, it should be placed at the line before FROM instruction
    # e.g. from build/gm/Dockerfile
    #   # _speed_buildx_for_go_
    #   FROM golang:1.14-alpine3.11 AS builder

    # go speed tag
    _speed_buildx_for_go_

  )

  local base_cmds='
# These ARGs are built in docker build
# see https://docs.docker.com/engine/reference/builder/#automatic-platform-args-in-the-global-scope
ARG TARGETPLATFORM BUILDPLATFORM TARGETARCH TARGETOS

RUN echo "I am running on $BUILDPLATFORM, building for $TARGETPLATFORM(=$TARGETOS/$TARGETARCH).."
'

  # FROM command pattern
  local from_pattern='^\s*FROM\s'  # 0+ leading whitespace, 'FROM' word, a whitespace
  local from_part='FROM --platform=$BUILDPLATFORM '  # note tailing space

  for speed_tag in "${speed_tags[@]}"; do
    speed_tag_pattern="^# ${speed_tag}$"

    if ! grep -q "$speed_tag_pattern" "$input_dockerfile"; then
      continue
    fi

    speed_cmds="$($speed_tag)"

    cross_cmds="
# generated by sedna::buildx
$base_cmds
$speed_cmds
# end generated by sedna::buildx
"

    awk -v cross_cmds="$cross_cmds" -v from_part="$from_part" \
      "{
         if (/$speed_tag_pattern/) {
           tag_found = 1
         }
         if (tag_found && sub(/$from_pattern/, from_part)) {
           # add from_part to the FROM command, and print
           print
           print cross_cmds
           tag_found = 0
         } else {
           print
         }
      }" "$input_dockerfile"

    # only support one language for one Dockerfile
    return
  done

  # if not speed tag added, just output its
  cat "$input_dockerfile"
}

sedna::buildx::push-multi-platform-images() {
  sedna::buildx::prepare_env

  bash scripts/storage-initializer/push_image.sh

  for component in ${COMPONENTS[@]}; do
    echo "pushing ${PLATFORMS} image for $component"

    temp_dockerfile=build/${component}/buildx_dockerfile
    sedna::buildx:generate-speed-dockerfile build/${component}/Dockerfile > ${temp_dockerfile}

    docker buildx build --push \
      --build-arg GO_LDFLAGS=${GO_LDFLAGS} \
      --platform ${PLATFORMS} \
      -t ${IMAGE_REPO}/sedna-${component}:${IMAGE_TAG} \
      -f ${temp_dockerfile} .

    rm ${temp_dockerfile}
  done
}

sedna::buildx::build-multi-platform-images() {
  sedna::buildx::prepare_env

  mkdir -p ${OUT_IMAGESPATH}
  arch_array=(${PLATFORMS//,/ })

  for component in ${COMPONENTS[@]}; do
    echo "building ${PLATFORMS} image for ${component}"
    buildx_dockerfile=${OUT_IMAGESPATH}/${component}-buildx-Dockerfile

    sedna::buildx:generate-speed-dockerfile build/${component}/Dockerfile > ${buildx_dockerfile}

    for arch in ${arch_array[@]}; do
      dest_tar=${OUT_IMAGESPATH}/${component}-${IMAGE_TAG}-${arch////-}.tar
      echo "building ${arch} image for ${component} and the image will be saved in ${dest_tar}"

      docker buildx build -o type=docker,dest=${dest_tar} \
        --build-arg GO_LDFLAGS=${GO_LDFLAGS} \
        --platform ${arch} \
        -t ${IMAGE_REPO}/sedna-${component}:${IMAGE_TAG} \
        -f ${buildx_dockerfile} .
      done
  done

}
