#!/bin/bash -e
#
# Provides commonly used functions and variables

_get_version() {
    SRC_OPENEMS_CONSTANTS="${SRC_OPENEMS_CONSTANTS:-io.openems.common/src/io/openems/common/OpenemsConstants.java}"
    SRC_PACKAGE_JSON="${SRC_PACKAGE_JSON:-ui/package.json}"
    SRC_PACKAGE_LOCK_JSON="${SRC_PACKAGE_LOCK_JSON:-ui/package-lock.json}"
    SRC_CHANGELOG_CONSTANTS="{SRC_CHANGELOG_CONSTANTS:-ui/src/app/changelog/view/component/changelog.constants.ts}"

    if [ -f "${SRC_PACKAGE_JSON}" ]; then
        UI_VERSION="$(grep '"version": '  ${SRC_PACKAGE_JSON} | head -n1 | sed 's/.*"version":[[:space:]]*"\([^"]*\)".*/\1/')"
    fi;

    if [ -f "${SRC_OPENEMS_CONSTANTS}" ]; then
        JAVA_VERSION_MAJOR="$(grep 'VERSION_MAJOR =' ${SRC_OPENEMS_CONSTANTS} | head -n1 | sed 's/.*VERSION_MAJOR[[:space:]]*=[[:space:]]*\([0-9][0-9]*\).*/\1/')"
        JAVA_VERSION_MINOR="$(grep 'VERSION_MINOR =' ${SRC_OPENEMS_CONSTANTS} | head -n1 | sed 's/.*VERSION_MINOR[[:space:]]*=[[:space:]]*\([0-9][0-9]*\).*/\1/')"
        JAVA_VERSION_PATCH="$(grep 'VERSION_PATCH =' ${SRC_OPENEMS_CONSTANTS} | head -n1 | sed 's/.*VERSION_PATCH[[:space:]]*=[[:space:]]*\([0-9][0-9]*\).*/\1/')"
        JAVA_VERSION_STRING="$(grep 'VERSION_STRING =' ${SRC_OPENEMS_CONSTANTS} | head -n1 | sed 's/.*VERSION_STRING[[:space:]]*=[[:space:]]*"\([^"]*\)".*/\1/')"
        JAVA_VERSION="${JAVA_VERSION_MAJOR}.${JAVA_VERSION_MINOR}.${JAVA_VERSION_PATCH}"
        if [ -n "$JAVA_VERSION_STRING" ]; then
            JAVA_VERSION="${JAVA_VERSION}-${JAVA_VERSION_STRING}"
        fi
    fi;

    if [ "$UI_VERSION" != "" ] && [ "$JAVA_VERSION" != "" ]; then
        if [ "$UI_VERSION" != "$JAVA_VERSION" ]; then
            echo "Error: Version mismatch between UI ($UI_VERSION) and Java ($JAVA_VERSION)!" >&2
            exit 1
        fi
    elif [ "$UI_VERSION" == "" ] && [ "$JAVA_VERSION" == "" ]; then
        echo "Error: Could not determine version from source files!" >&2
        exit 1
    fi;

    echo "${JAVA_VERSION:-$UI_VERSION}"
}

common_initialize_environment() {
    # Code files relevant for version
    SRC_OPENEMS_CONSTANTS="io.openems.common/src/io/openems/common/OpenemsConstants.java"
    SRC_PACKAGE_JSON="ui/package.json"
    SRC_PACKAGE_LOCK_JSON="ui/package-lock.json"
    SRC_CHANGELOG_CONSTANTS="ui/src/app/changelog/view/component/changelog.constants.ts"

    # Set environment variables
    THEME="${THEME:-openems}"
    PACKAGE_NAME="openems-edge"

    VERSION_STRING=""
    VERSION="$(_get_version)"
    local tmp_version=$(echo $VERSION | cut -d'-' -f1)
    VERSION_MAJOR=$(echo $tmp_version | cut -d'.' -f1)
    VERSION_MINOR=$(echo $tmp_version | cut -d'.' -f2)
    VERSION_PATCH=$(echo $tmp_version | cut -d'.' -f3)
    VERSION_STRING=$(echo $VERSION | cut -s -d'-' -f2)
    VERSION_FULL=$tmp_version
}

common_build_snapshot_version() {
    if [[ "$VERSION" == *"-SNAPSHOT" ]]; then
        # Replace unwanted characters with '.', compliant with Debian version
        # Ref: https://unix.stackexchange.com/a/23673
        if [[ -n "$CI_COMMIT_BRANCH" ]]; then
            VERSION_DEV_BRANCH="${CI_COMMIT_BRANCH}"
        else
            VERSION_DEV_BRANCH="$(git branch --show-current)"
        fi
        VERSION_DEV_COMMIT=""
        if git diff --exit-code --quiet; then
            VERSION_DEV_COMMIT="$(git rev-parse --short HEAD)"
        else
            VERSION_DEV_COMMIT="dirty"
        fi
        VERSION_DEV_BUILD_TIME=$(date "+%Y%m%d.%H%M")
        # Compliant with https://www.debian.org/doc/debian-policy/ch-controlfields.html#s-f-version
        VERSION_STRING="$(echo $VERSION_DEV_BRANCH | tr -cs 'a-zA-Z0-9\n' '.').${VERSION_DEV_BUILD_TIME}.${VERSION_DEV_COMMIT}"
        VERSION="${VERSION/-SNAPSHOT/"-${VERSION_STRING}"}"
    fi
}

# Inserts the version number into the Code
common_update_version_in_code() {
    echo "# Update version in Code"
    if [[ -f "$SRC_OPENEMS_CONSTANTS" ]]; then
        echo "## Update $SRC_OPENEMS_CONSTANTS"
        sed --in-place "s#\(VERSION_MAJOR = \)\([0-9]\+\);#\1$VERSION_MAJOR;#" $SRC_OPENEMS_CONSTANTS
        sed --in-place "s#\(VERSION_MINOR = \)\([0-9]\+\);#\1$VERSION_MINOR;#" $SRC_OPENEMS_CONSTANTS
        sed --in-place "s#\(VERSION_PATCH = \)\([0-9]\+\);#\1$VERSION_PATCH;#" $SRC_OPENEMS_CONSTANTS
        sed --in-place "s#\(VERSION_STRING = \)\"\(.*\)\";#\1\"$VERSION_STRING\";#" $SRC_OPENEMS_CONSTANTS
        sed --in-place "s#\(VERSION_DEV_BRANCH = \)\"\(.*\)\";#\1\"${VERSION_DEV_BRANCH}\";#" $SRC_OPENEMS_CONSTANTS
        sed --in-place "s#\(VERSION_DEV_COMMIT = \)\"\(.*\)\";#\1\"$VERSION_DEV_COMMIT\";#" $SRC_OPENEMS_CONSTANTS
        sed --in-place "s#\(VERSION_DEV_BUILD_TIME = \)\"\(.*\)\";#\1\"$VERSION_DEV_BUILD_TIME\";#" $SRC_OPENEMS_CONSTANTS
    fi
    
    if [[ -f "$SRC_PACKAGE_JSON" ]]; then
        echo "## Update $SRC_PACKAGE_JSON"
        sed --in-place '/"name": "openems-ui"/{n;s#\(^.*"version": "\)[^"]*\(".*\)#\1'"$VERSION"'\2#;}' $SRC_PACKAGE_JSON
    fi
    
    if [[ -f "$SRC_PACKAGE_LOCK_JSON" ]]; then
        echo "## Update $SRC_PACKAGE_LOCK_JSON"
        sed --in-place '/"name": "openems-ui"/{n;s#\(^.*"version": "\)[^"]*\(".*\)#\1'"$VERSION"'\2#;}' $SRC_PACKAGE_LOCK_JSON
    fi

    if [[ -f "$SRC_CHANGELOG_CONSTANTS" ]]; then
        echo "## Update $SRC_CHANGELOG_CONSTANTS"
        sed --in-place "s#\(UI_VERSION = \"\).*\(\";\)#\1$VERSION\2#" $SRC_CHANGELOG_CONSTANTS
    fi
}

common_print_banner() {
    local text="$1"
    local len="${#text}"
    printf "\n"
    printf ' %*s \n' "$len" '' | tr ' ' '='
    printf ' %s \n' "$text"
    printf ' %*s \n' "$len" '' | tr ' ' '='
}

# Build OpenEMS Backend
common_build_backend() {
    common_print_banner "Build OpenEMS Backend"
    ./gradlew "$@" --build-cache buildBackend resolve.BackendApp
    git diff --exit-code io.openems.backend.application/BackendApp.bndrun
}

# Build OpenEMS Edge and UI in parallel
common_build_edge_and_ui_in_parallel() {
    # TODO use 'parallel' tool for reliable implementation
    common_build_edge
    common_build_ui
}


# Build OpenEMS Edge
common_build_edge() {
    common_print_banner "Build OpenEMS Edge"
    ./gradlew "$@" --build-cache buildEdge resolve.EdgeApp
    git diff --exit-code io.openems.edge.application/EdgeApp.bndrun
}

# Run OpenEMS Checkstyle
common_run_checkstyle() {
    common_print_banner "Run Checkstyle"
    ./gradlew "$@" checkstyleAll
}

# Run OpenEMS Edge Checkstyle
common_checkstyle_edge() {
    common_print_banner "Run Edge Checkstyle"
    ./gradlew "$@" checkstyleEdge
}

# Run OpenEMS Backend Checkstyle
common_checkstyle_backend() {
    common_print_banner "Run Backend Checkstyle"
    ./gradlew "$@" checkstyleBackend
}

# Run OpenEMS Edge Tests
common_test_edge() {
    common_print_banner "Run OpenEMS Edge JUnit Tests"
    ./gradlew "$@" --build-cache testEdge
}

# Run OpenEMS Backend Tests
common_test_backend() {
    common_print_banner "Run OpenEMS Backend JUnit Tests"
    ./gradlew "$@" --build-cache testBackend
}

# Run OpenEMS Tests
common_test() {
    common_print_banner "Run OpenEMS JUnit Tests"
    ./gradlew "$@" --build-cache test
}

# Build OpenEMS UI
common_build_ui() {
    common_print_banner "Build OpenEMS UI"
    if [ "${NODE_MODULES_CACHE}" != "" -a -d "$NODE_MODULES_CACHE" ]; then
        echo "## Use cached node_modules"
        mv -f "${NODE_MODULES_CACHE}" "ui/node_modules"
    fi
    cd ui

    # Install dependencies from package.json
    npm ci
    if [ "${NG_CLI_CACHE_PATH}" != "" ]; then
        echo "## Angular Cache: $NG_CLI_CACHE_PATH"
        node_modules/.bin/ng config cli.cache.path "$NG_CLI_CACHE_PATH"
    fi
    # Lint and Build
    node_modules/.bin/ng lint
    node_modules/.bin/ng build -c "${THEME},${THEME}-edge-prod,prod"
    cd ..

    if [ "${NODE_MODULES_CACHE}" != "" ]; then
        echo "## Refresh node_modules cache"
        mv -f "ui/node_modules" "${NODE_MODULES_CACHE}"
    fi
}

common_build_android_app() {
    echo "# Build OpenEMS Android APP"
    if [ "${NODE_MODULES_CACHE}" != "" -a -d "$NODE_MODULES_CACHE" ]; then
        echo "## Use cached node_modules"
        mv -f "${NODE_MODULES_CACHE}" "ui/node_modules"
    fi
    cd ui

    # Install dependencies from package.json
    npm ci
    if [ "${NG_CLI_CACHE_PATH}" != "" ]; then
        echo "## Angular Cache: $NG_CLI_CACHE_PATH"
        node_modules/.bin/ng config cli.cache.path "$NG_CLI_CACHE_PATH"
    fi

    case "${THEME^^}" in
        "EXAMPLE") NODE_ENV="EXAMPLE";;
    esac

    echo '[]' > src/assets/json/changelog.json

    # Install depencencies for capacitor
    NODE_ENV=${NODE_ENV} ionic cap build android -c "${THEME},${THEME}-backend-prod" --no-open

    # Build App
    cd android
    THEME=${THEME} bash ./gradlew buildThemeRelease

    cd ../..
}


# Builds, exports and uploads a ios app.
common_build_ios_app() {
    GIT_ROOT_DIR=$(git rev-parse --show-toplevel)
    THEME=""
    CONFIGURATION="Release"

    while getopts "t:" opt; do
    case $opt in
        t)
        THEME="$OPTARG"
        ;;
        *)
        echo "Usage: $0 -t theme"
        exit 1
        ;;
    esac
    done

    echo "Building ios app with $THEME and for $CONFIGURATION"
    cd ui

    # Install dependencies from package.json
    npm ci

    # Schemes respresent targets
    case "${THEME:u}" in
        "EXAMPLE") NODE_ENV="EXAMPLE" SCHEME="App" ;;
    esac

    # Build app
    NODE_ENV=${NODE_ENV} ./node_modules/.bin/ionic cap build ios -c "${THEME},${THEME}-backend-prod,prod" --no-open
    cd ios/App

    ARCHIVE_PATH="$(pwd)/output/$SCHEME.xcarchive"
    WORKSPACE="App.xcworkspace"

    # Install capacitor deps
    pod install 

    # Update marketing version
    ruby $GIT_ROOT_DIR/tools/deploy/ios/update_marketing_version.rb --scheme $SCHEME --target-name $TARGET

    # Unlock security keychain to access signing certificates and provisioning profiles
    security unlock-keychain -p $KEYCHAIN_PASSWORD login.keychain

    echo "\n====== Building & Archiving =====\n"
    
    xcodebuild \
        -workspace "$WORKSPACE" \
        -scheme "$SCHEME" \
        -configuration Release \
        -destination "generic/platform=iOS" \
        -archivePath "$ARCHIVE_PATH" \
        -allowProvisioningUpdates \
        -authenticationKeyID "$AUTHENTICATION_KEY_ID" \
        -authenticationKeyIssuerID "$AUTHENTICATION_KEY_ISSUER_ID" \
        -authenticationKeyPath "$AUTHENTICATION_KEY_PATH" \
        -quiet \
        archive

    ruby $GIT_ROOT_DIR/tools/deploy/ios/update_cf_bundle_version.rb --scheme $SCHEME --target-name $TARGET

    echo "\n====== Uploading ios App =====\n"

    xcodebuild -exportArchive \
        -allowProvisioningUpdates \
        -archivePath "$ARCHIVE_PATH" \
        -exportOptionsPlist ExportOptions.plist \
        -authenticationKeyID "$AUTHENTICATION_KEY_ID" \
        -authenticationKeyIssuerID "$AUTHENTICATION_KEY_ISSUER_ID" \
        -authenticationKeyPath "$AUTHENTICATION_KEY_PATH" \
        -quiet

}

create_ios_app_release(){
    GIT_ROOT_DIR=$(git rev-parse --show-toplevel)
    AUTHENTICATION_KEY_FILE=""
    THEME=""
    while getopts "t:a:" opt; do
    case $opt in
        t)
        THEME="$OPTARG"
        ;;
        a)
        AUTHENTICATION_KEY_FILE="$OPTARG"
        ;;
        *)
        echo "Usage: $0 -t theme -a $1"
        exit 1
        ;;
    esac
    done

    BUNDLE_ID=""
    case "${THEME^^}" in
    "FENECON") BUNDLE_ID="de.fenecon.fems" ;;
    "HECKERT") BUNDLE_ID="com.heckertsolar.ems" ;;
    esac

    echo "$AUTHENTICATION_KEY_ID"

    ruby $GIT_ROOT_DIR/tools/deploy/ios/create_release.rb --bundle-id "$BUNDLE_ID" --version "$VERSION_FULL" --authFile "$AUTHENTICATION_KEY_FILE"
}

common_save_environment() {
    local file=${1:-build.environment}
    echo "export VERSION=\"$VERSION\"
export VERSION_MAJOR=\"$VERSION_MAJOR\"
export VERSION_MINOR=\"$VERSION_MINOR\"
export VERSION_PATCH=\"$VERSION_PATCH\"
export VERSION_STRING=\"$VERSION_STRING\"
export VERSION_DEV_BRANCH=\"$VERSION_DEV_BRANCH\"
export VERSION_DEV_COMMIT=\"$VERSION_DEV_COMMIT\"
export VERSION_DEV_BUILD_TIME=\"$VERSION_DEV_BUILD_TIME\"
export VERSION_FULL=\"$VERSION_FULL\"" | tee $file
}

common_check_file() {
    local file=$1
    local error_message=${2:-"File not found!"}
    if [ ! -f "$file" ]; then
        echo "Error: $error_message"
        exit 1
    fi
}

common_create_version_file() {
    local file="${1:-fems.version}"
    if [ -f "$file" ]; then
        echo "File exists: $file; Skip!"
        return
    fi
    echo $VERSION > "$file"
}
