#!/usr/bin/env bash

rdo_header docker

rdo_docker() {
  local cmd="${1}"; shift || :

  case "${cmd}" in
    args)
      rdo__call_empty rdo_docker_args "${@}"
      ;;
    build)
      rdo_docker_build "${@}"
      ;;
    children)
      rdo_docker_children "${@}"
      ;;
    clean)
      rdo_included docker.clean || source "${PROJECT_PATH}/functions/docker.clean"
      rdo_docker_clean "${@}"
      ;;
    context)
      rdo_included docker.context || source "${PROJECT_PATH}/functions/docker.context"
      rdo_docker_context "${@}"
      ;;
    images)
      rdo__call_empty rdo_docker_images "${@}"
      ;;
    inspect)
      rdo_docker_inspect "${@}"
      ;;
    pull)
      rdo_docker_pull "${@}"
      ;;
    stage)
      rdo_docker_stage "${@}"
      ;;
    targets)
      rdo__call_empty rdo_docker_targets "${@}"
      ;;
    --help|-h|'')
      echo "Usage: do docker COMMAND"
      echo
      echo "Docker commands"
      echo
      echo "Commands"
      echo "  args      List all args in build targets"
      echo "  build     Build a container"
      echo "  targets   List all build targets"
      echo
      echo "Run 'do docker COMMAND --help' for more information on a command."
      ;;
    *)
      print_error "unknown docker command: ${@}"
      return 1
      ;;
  esac
}

rdo_docker_args() {
  cd "${PROJECT_PATH}/docker"

  local target_list=($(ls dockerfile.*[!~]))
  declare -A arg_list

  for arg in $(sed -nE 's/^ARG ([^=]*)[^$]*$/\1/p' "${target_list[@]}"); do
    arg_list[${arg}]="yes"
  done

  printf "%s\n" "${!arg_list[@]}"
}

rdo_docker_build() {
  local args=()
  local cmd_prefix=()

  local arg_ancestor_project=
  local arg_base_image=
  local arg_cache_base_tag=
  local arg_context="."
  local arg_dry_run=
  local arg_filename=
  local arg_no_rebuild=
  local arg_no_cache=
  local arg_rebuild=
  local arg_repository="${RDO_REPOSITORY:?Not in rdo bash session.}"
  local arg_stage_image=
  local arg_tag=
  local arg_tag_append=

  local arg_post_exec=()
  local arg_pre_exec=()
  local arg_test_exec=()

  while [[ -n "${1}" ]]; do
    case "${1}" in
      --ancestor-project)
        arg_ancestor_project="${2:?Missing ancestor project argument.}"
        shift 2 ;;
      --base-image)
        arg_base_image="${2:?Missing base image argument.}"
        shift 2 ;;
      --build-arg)
        args+=("--build-arg" "${2:?Missing build arg argument.}")
        shift 2 ;;
      --cache-base-tag)
        arg_cache_base_tag="${2:?Missing cache base tag argument.}"
        shift 2 ;;
      --context)
        arg_context="${2:?Missing context argument.}"
        shift 2 ;;
      --dry-run)
        arg_dry_run="yes"
        shift ;;
      --filename)
        arg_filename="${2:?Missing filename argument.}"
        shift 2 ;;
      --no-rebuild)
        arg_no_rebuild="yes"
        shift ;;
      --no-cache)
        arg_no_cache="yes"
        shift ;;
      --post-exec)
        arg_post_exec+=("${2:?Missing exec argument.}")
        shift 2 ;;
      --pre-exec)
        arg_pre_exec+=("${2:?Missing exec argument.}")
        shift 2 ;;
      --rebuild)
        arg_rebuild="yes"
        shift ;;
      --repository)
        arg_repository="${2:?Missing repository argument.}"
        shift 2 ;;
      --stage-image)
        arg_stage_image="${2:?Missing stage image argument.}"
        shift 2 ;;
      --tag)
        arg_tag="${2:?Missing tag argument.}"
        shift 2 ;;
      --tag-append)
        arg_tag_append="${2:?Missing tag argument.}"
        shift 2 ;;
      --test-exec)
        arg_test_exec+=("${2:?Missing exec argument.}")
        shift 2 ;;
      --help|-h)
        echo "Usage: rdo docker build "
        echo
        echo "Build docker container"
        echo
        echo "Options:"
        echo "      --base-image      Select base image"
        echo "      --build-arg       Pass build argument to docker"
        echo "      --dry-run         Do not actually build the container"
        echo "      --filename        Set dockerfile filename"
        echo "      --stage-image     Select stage image"
        echo "      --repository      Set repository name"
        echo "      --tag             Set the image tag"
        echo "      --tag-append      Append to the image tag"
        echo
        echo "Run 'do docker build --help' for more information on a command."
        return
      ;;
      *)
        if [[ -z "${1##-*}" ]] || (( ${#} != 1 )); then
          echo "do docker build: invalid parameters: ${@}" 2> /dev/null
          exit 1
        fi

        break
        ;;
    esac
  done

  local arg_1="${1:?Missing first argument.}"

  local filename="${arg_filename:-${arg_1%:*}}"
  local tag_full="${arg_repository}/${arg_tag:-${arg_1}}"

  if [[ -n "${arg_tag_append}" ]]; then
    if [[ ! "${tag_full}" =~ : ]]; then
      tag_full+=":${arg_tag_append}"
    else
      tag_full+="${arg_tag_append}"
    fi

    if [[ -n "${arg_cache_base_tag}" ]] && [[ ! "${arg_cache_base_tag}" =~ : ]]; then
      arg_cache_base_tag+=":${arg_tag_append}"
    fi
  fi

  args+=("--tag" "${tag_full}")
  args+=("--file" "./docker/dockerfile.${filename//\//.}")

  [[ -n "${arg_dry_run}" ]] && cmd_prefix+=("echo")
  [[ -n "${arg_no_cache}" ]] && args+=("--no-cache")

  [[ -n "${arg_ancestor_project}" ]]    && args+=("--build-arg" "ANCESTOR_PROJECT=${arg_ancestor_project}")
  [[ -n "${arg_base_image}" ]]          && args+=("--build-arg" "BASE_IMAGE=${arg_base_image}")
  [[ -n "${arg_cache_base_tag}" ]]      && args+=("--build-arg" "CACHE_BASE_TAG=${arg_cache_base_tag}")
  [[ -n "${arg_repository}" ]]          && args+=("--build-arg" "REPOSITORY=${arg_repository}")
  [[ -n "${arg_stage_image}" ]]         && args+=("--build-arg" "STAGE_IMAGE=${arg_stage_image}")
  [[ -n "${arg_tag_append}" ]]          && args+=("--build-arg" "TAG_APPEND=${arg_tag_append}")

  if [[ -z "${arg_rebuild}" ]] && [[ -n "${arg_no_rebuild}" ]] && [[ -n "$(rdo__image_id__name "${tag_full}")" ]]; then
    print_progress "\nskipping ${tag_full}, already exists"
    return 0
  fi

  print_progress "\nbuilding ${tag_full} from ${filename}"

  local test_cmd
  for test_cmd in "${arg_test_exec[@]}"; do
    print_progress "calling test exec command '${test_cmd} ${tag_full}'"

    if ! "${cmd_prefix[@]}" ${test_cmd} "${tag_full}"; then
      print_progress "skipping, not needed"
      return
    fi
  done

  # Change cwd to PROJECT_PATH so that docker doesn't get confused by
  # absolute windows paths that might be ambigious.
  cd "${PROJECT_PATH}"

  local pre_cmd
  for pre_cmd in "${arg_pre_exec[@]}"; do
    print_progress "calling pre exec command '${pre_cmd} ${tag_full}'"
    "${cmd_prefix[@]}" ${pre_cmd} "${tag_full}"
  done

  args+=(--progress plain)

  echo docker build "${args[@]}" "${arg_context}"

  if ! DOCKER_BUILDKIT=1 "${cmd_prefix[@]}" docker build "${args[@]}" "${arg_context}"; then
    print_error "failed to build docker container '${tag_full}'"
    exit 1
  fi

  local post_cmd
  for post_cmd in "${arg_post_exec[@]}"; do
    print_progress "calling post exec command '${post_cmd} ${tag_full}'"
    "${cmd_prefix[@]}" ${post_cmd} "${tag_full}"
  done
}

rdo_docker_children() {
  while [[ -n "${1}" ]]; do
    local parent_tag="${1:?Missing parent tag argument.}"
    local parent_id="$(docker inspect --format '{{ .Id }}' "${parent_tag}")"
    shift

    if [[ -z "${parent_id}" ]]; then
      print_error "parent tag '${parent_tag}' not found"
      exit 1
    fi

    local image_list=($(docker inspect --format '{{ .ID }}_{{ .Parent }}' $(docker images --all --no-trunc --quiet --filter label=ancestor_project=rdo-project)))
    local parent_list=()

    local iidx
    for (( iidx=${#image_list[@]}-1; iidx >= 0; iidx--)); do
      local image_pair=(${image_list[${iidx}]//_/ })

      local image_id="${image_pair[0]}"
      local image_parent="${image_pair[1]}"

      if [[ "${image_parent}" == "${parent_id}" ]]; then
        echo "${image_id}"
        parent_list+=("${image_id}")
        continue
      fi

      local pidx
      for (( pidx=0; pidx < ${#parent_list[@]}; pidx++ )); do
        local pidx_id="${parent_list[${pidx}]}"

        if [[ "${image_parent}" == "${pidx_id}" ]]; then
          echo "${image_id}"
          parent_list+=("${image_id}")
          break
        fi
      done
    done
  done
}

rdo_docker_images() {
  local image_list=($(docker images --all --no-trunc --quiet --filter label=ancestor_project=rtorrent-docker))
  printf "%s\n" "${image_list[@]}"
}

rdo_docker_inspect() {
  local arg_format=()

  while [[ -n "${1}" ]]; do
    case "${1}" in
      --id)
        arg_format+=("{{ .Id }}")
        shift ;;
      --parent)
        arg_format+=("{{ .Parent }}")
        shift ;;
      --help|-h)
        echo "Usage: rdo docker inspect COMMAND"
        echo
        echo "Inspect docker container"
        echo
        echo "Options:"
        echo "      --id      Print full hash id"
        echo "      --parent  Print full hash parent id"
        echo
        echo "Run 'do inspect COMMAND --help' for more information on a command."
      ;;
      *)
        if [[ -z "${1##-*}" ]]; then
          echo "do docker inspect: invalid parameters: ${@}" 2> /dev/null
          exit 1
        fi

        break
        ;;
    esac
  done

  if (( ${#} == 0 )); then
    return 0
  fi

  local image_list="$(printf " %s " $(rdo_docker images))"
  local matched_images=()

  for image_tag in "${@}"; do
    local image_id="$(docker inspect --format '{{ .Id }}' "${image_tag}")"

    if [[ -n "${image_id}" ]] && [[ "${image_list}" =~ " ${image_id} " ]]; then
      matched_images+=("${image_id}")
    fi
  done

  docker inspect --format "${arg_format[@]}" "${@}"
}

rdo_docker_pull() {
  local tag_name
  for tag_name in "${@}"; do
    if [[ -z "$(rdo__image_id__name "${tag_name}")" ]]; then
      docker pull "${tag_name}"
    fi
  done
}

rdo_docker_stage() {
  rdo_docker context build --context-type "stage" --context-name "stage" "${@}"
}

rdo_docker_targets() {
  cd "${PROJECT_PATH}/docker"

  local target_list
  target_list=($(ls dockerfile.*[!~]))
  target_list=("${target_list[@]#dockerfile.}")

  printf "%s\n" "${target_list[@]//\./\/}"
}
