#!/bin/bash

build::description() {
    echo "Build Syndesis"
}

build::usage() {
    cat <<"EOT"
-b  --backend                  Build only backend modules (core, extension, integration, connectors, server, meta)
    --all-images               Build all modules with images: ui-react, server, meta, s2i, operator, upgrade
    --app-images               Build only application modules with Docker images (ui-react, server, meta, s2i)
                               and create images
    --infra-images             Build only infrastructure modules with Docker images (operator, upgrade) and create images
-m  --module <m1>,<m2>, ..     Build modules
                               Modules: ui-react, server, connector, s2i, meta, integration, extension, common, operator, upgrade
                               Submodules: defined with [groupId]:artifactId (e.g. :connector-sql)
-d  --dependencies             Build also all project the specified module depends on
    --skip-tests               Skip unit, integration and system test execution
    --skip-checks              Disable all checks
-f  --flash                    Skip checks and tests execution (fastest mode)
-i  --image                    Build Docker via s2i images, too (for those modules creating images)
    --docker                   Use a plain Docker build for creating images. Used by CI for pushing to Docker Hub
    --operator-tag             The tag to use for the operator image
-p  --project <project>        Specifies the project / namespace to create images
-g  --goals <g1>,<g2>, ..      Use custom Maven goals to execute for the build. Default goal is `install`
-s  --settings <path>          Path to Maven settings.xml
-c  --clean                    Run clean builds (mvn clean)
    --incremental [ref]        Run an incremental build, i.e. skip building of a module if committed or uncomitted changes
                               against [ref] (default branch) are not part of that module
    --resume <m1>              Resume build from maven module (mvn -rf m1)
    --batch-mode               Run mvn in batch mode
    --camel-snapshot <version> Run a build with a specific Camel snapshot. The
                               environment variable CAMEL_SNAPSHOT_VERSION is used and you need to set it or an error will be thrown.
    --maven-mirror <url>       Use the maven mirror for the build, e.g. a maven repo proxy in OpenShift. If no
                               argument is given, the MAVEN_MIRROR_URL environment variable is used or
                               'http://nexus.myproject:8081/nexus/content/groups/public', if none is set.
                               See also https://docs.openshift.com/container-platform/3.5/dev_guide/app_tutorials/maven_tutorial.html
EOT

}

build::run() {

    # Maven helper funcs
    source "$(basedir)/commands/util/maven_funcs"

    # For OpenShift based builds
    source "$(basedir)/commands/util/openshift_funcs"

    # For operator building
    source "$(basedir)/commands/util/operator_funcs"

    local modules="$(extract_modules)"
    local top_dir="$(appdir .)"

    # All Maven modules (all modules minus operator, upgrade)
    local maven_modules=$(pick_module "$MAVEN_MODULES" "$modules")

    # All individual Maven submodules defined with [groupId]:artifactId (e.g. :connector-sql)
    local submodules=$(pick_submodule "$ALL_MODULES" "$modules")

    #
    # Check environments of modules to determine
    # if requirements are met
    #

    if $(should_build "operator" "$modules"); then
        local canbuildop=$(check_operator_requirements)
        if [ "$canbuildop" != "OK" ]; then
            echo
            echo "$canbuildop"
            echo
        fi
    fi

    #
    # Actually now do the build
    #

    if [ -z "$modules" ] || [ -n "$maven_modules" ]; then
        call_maven "$(maven_args)" "$maven_modules"
    fi

    if [ -n "$modules" ] && [ -n "$submodules" ]; then
        call_maven "$(maven_args)" "$submodules"
    fi

    # Build operator if requested
    if $(should_build "operator" "$modules") && [ "$canbuildop" = "OK" ]; then
        local tag=$(readopt --operator-tag)
        do_operator $top_dir "$tag"
    fi

    # Build image for upgrade pod if requested
    if $(should_build "upgrade" "$modules") && [ $(hasflag -i --image --docker) ]; then
        do_upgrade_pod $top_dir
    fi
}

maven_args() {
    local project=${1:-}
    local args="--no-transfer-progress"

    if [ $(hasflag --flash -f) ]; then
        args="$args -Pflash"
    fi

    if [ $(hasflag --skip-tests) ]; then
        args="$args -DskipTests -DskipITs"
    fi

    if [ $(hasflag --skip-checks) ]; then
        args="$args -Pskip-checks"
    fi

    if [ $(hasflag --batch-mode) ]; then
        args="$args --batch-mode"
    fi

    if [ $(hasflag --image -i --all-images --app-images --infra-images --docker) ]; then
        #Build images
        args="$args -Pimage"
        if [ $(hasflag --docker) ]; then
            args="$args -Dfabric8.mode=kubernetes"
        else
            args="$args -Dfabric8.mode=openshift"
        fi
    fi

    if [ $(hasflag --camel-snapshot) ]; then
       local version=$(readopt --camel-snapshot)
       if [ -z "$version" ]; then
          version="${CAMEL_SNAPSHOT_VERION:-}"
       fi
       if [ -z "$version" ]; then
          echo "ERROR: you need to provied an argument to --camel-snapshot or set the environment variable CAMEL_SNAPSHOT_VERSION to use --camel-snapshot option".
          exit 1
       fi
       args="$args -Dcamel.version=$version"
    fi

    if [ $(hasflag --maven-mirror) ]; then
        local maven_mirror=$(readopt --maven-mirror)
        if [ -z "$maven_mirror" ]; then
            maven_mirror=$(maven_mirror_url)
            check_error $maven_mirror
        fi
        args="$args -DmavenMirror=${maven_mirror}"
    fi

    if [ $(hasflag --settings -s) ]; then
        local maven_settings=$(readopt --settings -s)
        args+=" -s $maven_settings"
    fi

    if [ -z "$project" ]; then
        project="$(readopt --project -p)"
    fi
    if [ -n "${project}" ]; then
        args="$args -Dfabric8.namespace=${project}"
    fi

    local resume_from="$(readopt --resume)"
    if [ -n "${resume_from}" ]; then
        args="$args -rf ${resume_from}"
    fi

    if [ "$(hasflag --clean -c)" ]; then
        args="$args clean"
    fi

    local goals="$(readopt --goals -g)"
    if [ -n "${goals}" ]; then
        args="$args ${goals//,/ }"
    else
        args="$args install"
    fi

    echo $args
}

do_operator() {
    local top_dir=$1
    local tag=${2:-latest}
    local image="syndesis/syndesis-operator" # No reason to expose image name to customisation

    echo "=============================================================================="
    echo "Building syndesis-operator"
    echo "=============================================================================="

    pushd "$top_dir/install/operator" > /dev/null

    local args=""
    local source_gen="$(readopt --source-gen)"
    if [ -n "${source_gen}" ]; then
        args="$args --source-gen ${source_gen}"
    fi

    if [ ! $(hasflag --image -i --all-images --app-images --infra-images --docker) ]; then
      args+="--image-build skip"
    fi

    if [ $(hasflag --docker) ]; then
        ./build.sh $args --image-build=docker --image-name="${image}" --image-tag="$tag"
    else
        ./build.sh $args --image-build=s2i --image-name="${image}" --image-tag="$tag"
    fi
    popd >/dev/null
}

do_upgrade_pod() {
    local top_dir=$1
    echo "=============================================================================="
    echo "Building syndesis-upgrade"
    echo "=============================================================================="

    local cli_jar=${top_dir}/app/server/cli/target/syndesis-cli.jar
    if [ ! -f $cli_jar ]; then
        echo "Cannot find $cli_jar to include in the upgrade pod. Please run 'syndesis build -f -m server' to create it"
        exit 1
    fi

    local upgrade_src=${top_dir}/tools/upgrade
    local build_dir=$(mktemp -d)
    trap "rm -rf '$build_dir'" EXIT

    cp -r ${upgrade_src}/* ${build_dir}
    cp $cli_jar ${build_dir}

    echo "Creating image syndesis/syndesis-upgrade:latest"
    docker build --build-arg version=latest -t syndesis/syndesis-upgrade:latest ${build_dir}
}

is_minishift_docker_daemon() {
    if [ -n "${DOCKER_CERT_PATH:-}" ] && [ "${DOCKER_CERT_PATH//minishift/}" != "$DOCKER_CERT_PATH" ]; then
        echo "true"
    else
        echo "false"
    fi
}

mkdir_on_minishift() {
    local dirs="$@"
    for dir in $dirs; do
        minishift ssh "sudo su -c '[ -d $dir ] || (mkdir -p $dir && chown -R docker $dir)'"
    done
}

rsync_binary_from_minishift() {
    local operator_bin=${1}
    local local_target_dir=${2}

    echo "Rsync compiled $operator_bin back to $local_target_dir"

    rsync -az \
          -e "ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o IdentitiesOnly=yes -o PasswordAuthentication=no -i ~/.minishift/machines/minishift/id_rsa"\
          --progress \
          docker@$(minishift ip):${operator_bin} \
          $local_target_dir
}


# All modules, in the right build order
ALL_MODULES="common extension integration connector meta server s2i ui-react test operator upgrade"
MAVEN_MODULES="common extension integration connector meta server s2i ui-react test"
APP_IMAGES="ui-react server meta s2i"
INFRA_IMAGES="operator upgrade"
ALL_IMAGES="$APP_IMAGES $INFRA_IMAGES"
MODULES=(
  "upgrade:server common extension integration connector"
  "operator"
  "ui-react"
  "common"
  "extension:common"
  "integration:common extension"
  "connector:common extension integration"
  "meta:common extension integration connector"
  "server:common extension integration connector"
  "s2i:common extension integration connector server"
  "test:common extension integration connector server s2i"
)
MODULE_PATHS=(
  "upgrade:tools/upgrade/"
  "operator:install/operator/"
  "ui-react:app/ui-react/"
  "common:app/common/"
  "extension:app/extension/"
  "integration:app/integration/"
  "connector:app/connector/"
  "meta:app/meta/"
  "server:app/server/"
  "s2i:app/s2i/"
)

extract_modules() {
    local modules=""

    if [ "$(hasflag --backend -b)" ]; then
        modules="$modules common extension integration connector server meta"
    fi

    if [ "$(hasflag --all-images)" ]; then
        modules="$modules ui-react server meta s2i operator"
    fi

    if [ "$(hasflag --app-images)" ]; then
        modules="$modules $APP_IMAGES"
    fi

    if [ "$(hasflag --infra-images)" ]; then
        modules="$modules $INFRA_IMAGES"
    fi

    local arg_modules=$(readopt --module -m);
    if [ -n "${arg_modules}" ]; then
        modules="$modules ${arg_modules//,/ }"
    fi

    if [ -z "$modules" ]; then
      modules=$ALL_MODULES
    fi

    if [ "$(hasflag --incremental)" ]; then
      local changed_modules=""

      for module in $modules; do
        for module_path in "${MODULE_PATHS[@]}"; do
          local key=${module_path%%:*}
          local path=${module_path#*:}
          if [ "$module" == $key ] && [ "$(has_changes $path)" ]; then
            changed_modules="$changed_modules $module"
          fi
        done
      done

      modules=$changed_modules
    fi

    if [ "$(hasflag --dependencies -d)" ]; then
        local extra_modules=""
        for module in $modules; do
            for m in "${MODULES[@]}"; do
              local k=${m%%:*}
              if [ "$module" == $k ]; then
                  local v=${m#*:}
                  extra_modules="${extra_modules} $v"
              fi
            done
        done
        modules="$modules $extra_modules"
    fi
    if [ -z "$modules" ] && [ ! "$(hasflag --incremental)" ]; then
      echo "EVERYTHING"
      return
    fi
    # Unique modules
    local unique_modules=$(echo $modules | xargs -n 1 | sort -u | xargs | awk '$1=$1')
    echo $(order_modules "$unique_modules")
}

order_modules() {
    # Fix order
    local modules="$1"
    local toplevel=""
    local submodules=""

    for module in ${modules}; do
      # Check if module is one of our top level modules
      if [ "${ALL_MODULES//$module}" != "${ALL_MODULES}" ]; then
        toplevel="${toplevel} $module"
      else
        submodules="${submodules} $module"
      fi
    done

    # All modules in the proper order
    local ordered=$ALL_MODULES
    for cm in "${MODULES[@]}"; do
      local check_module=${cm%%:*}
      # Check if $check_module is in the list of unordered top level modules
      if [ -z "${toplevel}" ] || [ -n "${toplevel##*${check_module}*}" ]; then
        # No, so remove it from the return value
        ordered=${ordered//$check_module/}
      fi
    done

    if [ -n "$submodules" ]; then
        ordered="$ordered $submodules"
    fi

    # Normalize return value
    echo $ordered | awk '$1=$1'
}

pick_module() {
    local pick_list="$1"
    local modules="$2"

    local res=""
    for module in $modules; do
        if [ "${pick_list//$module/}" != "${pick_list}" ]; then
            res="$res $module"
        fi
    done
    echo $res | awk '$1=$1'
}

pick_submodule() {
    local toplevel="$1"
    local modules="$2"

    local res=""
    for module in $modules; do
        if [ "${toplevel//$module/}" == "${toplevel}" ]; then
            res="$res $module"
        fi
    done
    echo $res | awk '$1=$1'
}

should_build() {
    local module="$1"
    local modules="$2"
    if [ "EVERYTHING" == "$modules" ]; then
        # Empty --> Build everything
        echo "true"
    elif [ "${modules//$module}" != "${modules}" ]; then
        # Modules contains module
        echo "true"
    else
        echo "false"
    fi
}
