#!/bin/bash

# --------------------------------------------------------------------------
#                          OceanBase Copyright@2020
#                         https://www.oceanbase.com/
# ---------------------------------------------------------------------------
# Set JAVA_HOME or JRE_HOME if not already set, ensure any provided settings
# are valid and consistent with the selected start-up options and set up the
# endorsed directory. Author: Eric.Sun
# ---------------------------------------------------------------------------

# Make sure prerequisite environment variables are set

CMD_LINE_ARGS="$*";

# Check JDK Environment
if [[ $JAVA_HOME == '' && $JRE_HOME == '' ]]; then
	echo "Neither the JAVA_HOME nor the JRE_HOME environment variable is defined (Oracle JDK 1.8.0_3xx+)";
	echo "At least one of these environment variable is needed to run this program";
	exit 1;
fi

JAVA_SH="$JAVA_HOME/bin/java";
if [[ $JAVA_HOME != '' ]]; then
	if [[ ! -f $JAVA_SH ]]; then
		echo "The JAVA_HOME environment variable is NOT defined correctly as the \"$JAVA_HOME/bin/java\" is not found";
		exit 1;
	else
		JRE_HOME=$JAVA_HOME;
	fi
fi

JAVA_SH="$JRE_HOME/bin/java";
if [[ $JRE_HOME != '' && ! -f $JAVA_SH ]]; then
	echo "The JRE_HOME environment variable is NOT defined correctly as the \"$JRE_HOME/bin/java\" is not found";
	exit 1;	 	
fi

BASE_PATH=$(cd $(dirname "$0"); pwd);
PARENT_PATH=$(dirname "$BASE_PATH");

CLASSPATH=".";
for JAR_PATH in $(find "$PARENT_PATH/lib/" -type f -name '*.jar'|sort);
do
    CLASSPATH=$CLASSPATH":"$JAR_PATH
done

CLASSPATH=$CLASSPATH":"$PARENT_PATH/conf/

# ####################################################################################
#        This shell snippets is used for setting JAVA OPTS: -Xms, -Xmx and GC
#                          Support on Linux/MacOSX
# ####################################################################################
VM_OPTS="-Xms4G -Xmx4G"

# Function to validate and set memory size
validate_and_set_memory() {
    local mem_value="$1"
    if [[ $mem_value =~ ^[1-9][0-9]*[KkMmGgTt]?$ ]]; then
        VM_OPTS="-Xms${mem_value} -Xmx${mem_value}"
        echo "You have successfully set the memory value: $mem_value"
    else
        echo "Error: The memory value format is incorrect. It should be in units of numbers plus K, M, G or T."
        exit 1
    fi
}

for (( i=1; i <= $#; i++ )); do
    arg="${!i}"
    # Check for --mem with a separate value
    if [[ $arg == "--mem" ]]; then
        if (( i >= $# )); then
            echo "Error: Memory value needs to be specified after --mem"
            exit 1
        fi
        ((i++))
        mem_size="${!i}"
        validate_and_set_memory "$mem_size"
        continue
    fi

    # Check for --mem=value
    if [[ $arg =~ ^--mem= ]]; then
        mem_size="${arg#*--mem=}"
        validate_and_set_memory "$mem_size"
        continue
    fi
done


GC_OPTS="-XX:+UseG1GC"

# ####################################################################################
#                               Set GC_OPTS by JAVA VERSION
#                        Option: -XX:+UseG1GC used for JDK >1.8.0_300
#                  Option: -XX:+UseConcMarkSweepGC used for JDK <1.8.0_300
# ####################################################################################
JAVA_VERSION=$($JAVA_SH -version 2>&1 | sed '1!d' | sed -e 's/"//g' | awk '{print $3}');
if [[ "${JAVA_VERSION}" != '' ]]; then
    MAJOR_VERSION=$(echo "${JAVA_VERSION}" | awk -F'_' '{print $1}');
    # shellcheck disable=SC2077
    if [[ "${MAJOR_VERSION}" == '1.8.0' ]]; then
        MINOR_VERSION=$(echo "${JAVA_VERSION}" | awk -F'_' '{print $2}');
        if [[ ${MINOR_VERSION} -lt 300 ]]; then
            GC_OPTS="-XX:+UseConcMarkSweepGC"
        fi
    fi
else
    echo -e "The java -version is <UNKNOWN>. Please check java command is available";
fi


OS_NAME=$(uname);
# shellcheck disable=SC2077
if [[ "${OS_NAME}" == 'Linux' ]]; then
    if [[ $EXPERIMENTAL -eq 1 ]]; then
        # ####################################################################################
        #                        (Experimental)
        #                        Set VM_OPTS by System Memory
        #                       -Xms,-Xmx = min(48G, max(1GB, mem * 0.6))
        #
        #                        Set GC_THREAD_OPTS/CI_THREAD_OPTS by CPU Processors
        #                       -XX:CICompilerCount, -XX:ParallelGCThreads = max(8, CPU_THREADS)
        # ####################################################################################
        CPU_THREADS=$(cat /proc/cpuinfo | grep "processor" | wc -l);
        if [[ $CPU_THREADS -gt 8 ]]; then
            CPU_THREADS=8;
        fi
        GC_OPTS="$GC_OPTS -XX:CICompilerCount=$CPU_THREADS -XX:ParallelGCThreads=$CPU_THREADS";

        FREE_MEM=$(free mem | grep 'Mem' | awk '{print $4}');
        # shellcheck disable=SC2004
        ALLOC_MEM=$(($FREE_MEM * 6 / 10485760));
        # echo -e "Free Mem: ${FREE_MEM}\tAlloc Mem: ${ALLOC_MEM}GB"
        if [[ $ALLOC_MEM -lt 1 ]]; then
            echo -e "Error: Allocated memory is less than 1GB";
            exit 1;
        else
            if [[ $ALLOC_MEM -gt 48 ]]; then
                ALLOC_MEM=48;
            fi
            VM_OPTS="-Xms${ALLOC_MEM}G -Xmx${ALLOC_MEM}G";
        fi
        echo -e "JAVA_OPTS: $JAVA_VERSION $MAJOR_VERSION $MINOR_VERSION $GC_OPTS $VM_OPTS";
    fi
else
    echo -e "Warn: \"${OS_NAME}\" operating system doesn't support dynamic settings";
fi

JAVA_OPTS="$JAVA_OPTS -server $VM_OPTS -XX:MetaspaceSize=128M -XX:MaxMetaspaceSize=128M -Xss512K";
JAVA_OPTS="$JAVA_OPTS $GC_OPTS -Xnoclassgc -XX:MaxGCPauseMillis=50";
JAVA_OPTS="$JAVA_OPTS -XX:+HeapDumpOnOutOfMemoryError -Dsun.stdout.encoding=UTF-8 -Dsun.stderr.encoding=UTF-8";

PROGRAM_OPTS="-Dpicocli.usage.width=180";

# This variable whether to enable parallel write.
# OBDumper will export multiple files concurrently based on sliced subtasks to improves the overall performance.
# But there is no guarantee that the data will be evenly distributed among those dumped files.
PROGRAM_OPTS="${PROGRAM_OPTS} -Denable.parallel.write=false";

# Table names must not contain the '.' character to prevent ambiguity in filenames during import
# Note:	This check aims to prevent ambiguity when use OBLoader to import from dumped files;
# a table name containing '.' would make it impossible to extract the real table name from the filename upon import, thus failing to correctly map to the database table
# e.g. table: `test.1` may yield 3 possible output depends on dump options, which is, `test.1.csv`, `test.1.0.csv` or `test.1.1.0.csv`.
# If you intend to do so as the potential risk is well acknowledged, feel free to skip this check.
PROGRAM_OPTS="${PROGRAM_OPTS} -Dskip.tableName.check=false";

# This variable decides what buffer to use when dump data to cloud object storage, including amazon s3, aliyun oss and any other s3-compat service.
# The options are:
# - disk: Use local disk as buffer. Capacity is limited to available disk space. Use cli arg `--tmp-path` to specify the buffer path.
# - bytebuffer: Use an in-memory bytebuffer. Fast and require no capacity limit for disk spaces, but will cost heap memory significantly.
PROGRAM_OPTS="${PROGRAM_OPTS} -Dupload.buffer.type=disk";

# This variable decides the buffer size of one block in bytes.
PROGRAM_OPTS="${PROGRAM_OPTS} -Dupload.buffer.size=67108864";

# This variable decides how many blocks can exist at the same time of a single writer(table-based or subtask-based, depends on the value of `enable.parallel.write`).
PROGRAM_OPTS="${PROGRAM_OPTS} -Dupload.active.blocks=2";

# Configures the s3 client to disable chunked encoding for all requests.
# Note: Enabling this option has performance implications since the checksum for the payload will have to be pre-calculated before sending the data.
# Using this option is recommended only if your endpoint does not implement chunked uploading.
PROGRAM_OPTS="${PROGRAM_OPTS} -Dupload.disable.chunked.encoding=false";

# This variable determines whether the exported Produce and Function definitions in MySQL mode contain definer information.
PROGRAM_OPTS="${PROGRAM_OPTS} -Denable.definer=true";

# This variable decides whether to enable sql monitor log.
PROGRAM_OPTS="${PROGRAM_OPTS} -DsqlMonitor.enabled=true"
# This variable decides the threshold to report a slow sql. Unit: milliseconds.
PROGRAM_OPTS="${PROGRAM_OPTS} -DsqlMonitor.slowSql.threshold=3000"

# This variable determines whether to export the indexes included in the Oracle table definition.
PROGRAM_OPTS="${PROGRAM_OPTS} -Denable.table.index=true";
# This variable determines whether to export the comments included in the Oracle table definition.
PROGRAM_OPTS="${PROGRAM_OPTS} -Denable.table.comment=true";
# This variable determines whether to export the comments contained in the Oracle columns defined by the table.
PROGRAM_OPTS="${PROGRAM_OPTS} -Denable.table.column.comment=true";

PROGRAM_OPTS="${PROGRAM_OPTS} -Dtool.base.dir=$PARENT_PATH";
# PROGRAM_OPTS="${PROGRAM_OPTS} -Dobproxy.configurationFile=$PARENT_PATH/conf/secure.crt";
PROGRAM_OPTS="${PROGRAM_OPTS} -Dsession.configurationFile=$PARENT_PATH/conf/session.config.json";
PROGRAM_OPTS="${PROGRAM_OPTS} -Dsecurity.configurationFile=$PARENT_PATH/conf/security.properties";
PROGRAM_OPTS="${PROGRAM_OPTS} -Dlog4j.output=$PARENT_PATH/logs -Dlog4j2.formatMsgNoLookups=true -Dlog4j.configurationFile=file://$PARENT_PATH/conf/log4j2.xml";

JAVA_CMD="$JAVA_SH $JAVA_OPTS $PROGRAM_OPTS -classpath $CLASSPATH com.oceanbase.tools.loaddump.cmd.Obdumper";

if [[ $DEBUG_MODE -eq 1 ]]; then
    PROGRAM_OPTS="${PROGRAM_OPTS} -Dlogback.configurationFile=$PARENT_PATH/conf/jdbc-trace.xml";
    echo -e "JAVA_CMD: $JAVA_CMD $CMD_LINE_ARGS";
fi
eval '$JAVA_CMD "$@"';