#!/bin/bash
# shellcheck disable=SC1090
# Various Build utils ##############################################################################################

export RED='\033[0;31m'
export GREEN='\033[0;32m'
export YELLOW='\033[1;33m'
export WHITE='\033[1;37m'
export NC='\033[0m'
export LIGHT_BLUE='\033[38;5;111m'
export CYAN='\033[0;36m'
export PINK='\033[38;5;198m'
export MAGENTA_BOLD='\033[1;35m'

# retrieve the number of executing processors
export NCPU="$(getconf _NPROCESSORS_ONLN 2> /dev/null)"

# The current path is also configured in TeamCity configuration:
# "settings -> Dependencies -> Artifact Dependencies"
export RESULT_CHANGED_BUILD_PATH_DIR="temp/prev_libs"
export RESULT_CHANGED_BUILD_PATH_FILE="${RESULT_CHANGED_BUILD_PATH_DIR}/res_changed_build_library.txt"

# Warning log in system temporary directory
export SYS_TMP_DIR=$(dirname "$(mktemp --dry-run)")
if [ -z "$WARNINGS_TMP" ]; then
    export WARNINGS_TMP="${SYS_TMP_DIR}"/scrypto_warnings.log
fi

# Prints call stack of function, script path and line where function was called
function get_stack()
{
    echo "Stack trace:"
    # to avoid noise we start with 1 to skip get_stack caller
    local i
    local stack_size=${#FUNCNAME[@]}
    for (( i=1; i<"$stack_size" ; i++ )); do
        local func="${FUNCNAME[$i]}"
        [ x"$func" = x ] && func=MAIN
        local linen="${BASH_LINENO[(( i - 1 ))]}"
        local src="${BASH_SOURCE[$i]}"
        [ x"$src" = x ] && src=non_file_source
        printf "\tFunction: %-20s    File: %s    Line: %s\n" "$func" "$src" "$linen"
    done
}

# Prints text with LIGHT_BLUE marker: [INFO] <text>
function print_info()
{
    echo -e "${LIGHT_BLUE}[INFO]${NC}" "$@"
}

# Prints text with RED marker: [ERROR] <text>
function print_error()
{
    echo -e "${RED}[ERROR]${NC}" "$@" >&2
    get_stack
    exit 1
}

# Prints text with GREEN marker: [SUCCESS] <text>
function print_success()
{
    echo -e "${GREEN}[SUCCESS]${NC}" "$@"
}

# Prints text with YELLOW marker: [WARNING] <text>
# Prints to console and to temporary file $WARNINGS_TMP
function print_warning()
{
    echo -e "${YELLOW}[WARNING]${NC}" "$@" | tee -a "$WARNINGS_TMP"
}

# If 0 - Prints [SUCCESS] label with specified message.
# If !0 - Prints [ERROR] label with specified message.
# Parameters:
#       $1 - return code of operation to check.
#       $2 - Message to print.

# Usage: use $? as $q parameter right after operation that you want to check.
# Use second argument as message to print description.
# Example:
#            ../<some_folder>/some_script.sh <with_some_parameters>
#            check_return_code $? "Some script with some parameters experienced some errors."
#
function check_return_code()
{
    if [ "$1" != "0" ]
    then
        print_error "Code: $1. $2"
        exit "$1"
    else
        print_success "Code: $1. $2"
    fi
}

function contain_element()
{
    local e

    for e in "${@:2}";
    do
        [[ "$e" == "$1" ]] && return 0
    done

    return 1
}

# Pass arguments as strings: check_required "Error message" "$PARAM_TO_CHECK"
function check_required()
{
    if [ "$2" == "" ]; then
        print_error "Message: $1"
        exit 1
    fi
}

function check_existing()
{
    check_arg_num $# 2 "function check_existing()"

    if [ ! -e "$2" ]; then
        print_error "Message: $1 Not existing!"
        exit 1
    fi
}

# Source files
# $1 file to source
# $2 alternative file to source
# source $1 if $1 != "". Otherwise - source $2
# if $1 not "" and not exist on disc, return 1. Otherwise - return 0;
# exit with code 1, if $1 != "" and no defaults exist
function source_file()
{
    check_arg_num $# 2 "function source_file()"

    if [ "$1" != "" ]; then
        if [ -e "$1" ]; then
            print_info "Source: $1"
            source "${1}"
        else
            print_warning "Can not source file: $1"
            return 1
        fi
    else
        if [ -e "$2" ]; then
            print_info "Source default: $2"
            source "${2}"
        else
            print_error "Bad usage. Default file not exist: $2"
            exit 2
        fi
    fi

    return 0
}

# Usage: check_arg_num $# 3 "Expected min 3 arguments in <somewhere>"
function check_arg_num()
{
    if [ "$#" -ne 3 ]; then
        print_error "Bad usage of check_arg_num: $# args, but 3 expected."
        exit 1
    fi

    if [ "$1" -lt "$2" ]; then
        print_error "Illegal number of arguments. Message: $3"
        exit 1
    fi
}

# get scrypto version info from string of version:
# FIPS_SCRYPTO_MODULE_VERSION_STR from header /openssl/scrypto_version.h
#
# arguments:
# $$    internal argument name              type    description
# ----------------------------------------------------------------------------------
# $1    __ARG_GSVI_VERSION_STR              INPUT   version string
# $2    __ARG_GSVI_MAJOR_VERSION            OUTPUT  major version
# $3    __ARG_GSVI_MINOR_VERSION            OUTPUT  minor version
# $4    __ARG_GSVI_EXTENDED_VERSION         OUTPUT  extended version
# $5    __ARG_GSVI_TYPE_EXTENDED_VERSION    OUTPUT  type of extended version
# ----------------------------------------------------------------------------------
function get_scrypto_version_info()
{
    # VERSION_STR  MAJOR_VERSION  MINOR_VERSION  TYPE_EXTENDED_VERSION  EXTENDED_VERSION
    # x.y.z        x              y              DEV                    z
    # x.y.beta.z   x              y              BETA                   z
    # x.y.RC.z     x              y              RC                     z
    # x.y          x              y              RELEASE                0
    # x.y.SR.z     x              y              SR                     z

    local __ARG_GSVI_VERSION_STR=$1
    local __ARG_GSVI_MAJOR_VERSION=$2
    local __ARG_GSVI_MINOR_VERSION=$3
    local __ARG_GSVI_EXTENDED_VERSION=$4
    local __ARG_GSVI_TYPE_EXTENDED_VERSION=$5

    #rematch pos                 ../..#1..\../..#2..\/#3\
    local __GSVI_GEN_VER_PATTERN="^([0-9]+)\.([0-9]+)(.*)"
    local __GSVI_REMATCH_POS_MJR_VER=1
    local __GSVI_REMATCH_POS_MNR_VER=2
    local __GSVI_REMATCH_POS_TAIL_GEN_VER=3

    #rematch pos                   ../..#1..\...../....#2....\../..#3..\..
    local __GSVI_EXT_VER_PATTERN="^\.([0-9]+)$|^\.(beta|RC|SR)\.([0-9]+)$"
    local __GSVI_REMATCH_POS_NUM_EXT_VER=1
    local __GSVI_REMATCH_POS_TYPE_EXT_VER=2
    local __GSVI_REMATCH_POS_TYPE_VAL_EXT_VER=3

    local __GSVI_TEMP_MJR_VER=""
    local __GSVI_TEMP_MNR_VER=""

    if [[ "${__ARG_GSVI_VERSION_STR}" =~ ${__GSVI_GEN_VER_PATTERN} ]]; then
        # x.y
        __GSVI_TEMP_MJR_VER=${BASH_REMATCH[${__GSVI_REMATCH_POS_MJR_VER}]}
        __GSVI_TEMP_MNR_VER=${BASH_REMATCH[${__GSVI_REMATCH_POS_MNR_VER}]}

        if [ -n "${BASH_REMATCH[${__GSVI_REMATCH_POS_TAIL_GEN_VER}]}" ]; then
            if [[ "${BASH_REMATCH[${__GSVI_REMATCH_POS_TAIL_GEN_VER}]}" =~ ${__GSVI_EXT_VER_PATTERN} ]]; then

                eval $__ARG_GSVI_MAJOR_VERSION='${__GSVI_TEMP_MJR_VER}'
                eval $__ARG_GSVI_MINOR_VERSION='${__GSVI_TEMP_MNR_VER}'

                if [ -n "${BASH_REMATCH[${__GSVI_REMATCH_POS_NUM_EXT_VER}]}" ]; then
                    # x.y.z
                    eval $__ARG_GSVI_TYPE_EXTENDED_VERSION="DEV"
                    eval $__ARG_GSVI_EXTENDED_VERSION='${BASH_REMATCH[${__GSVI_REMATCH_POS_NUM_EXT_VER}]}'
                else
                    # x.y.type.z
                    eval $__ARG_GSVI_TYPE_EXTENDED_VERSION='${BASH_REMATCH[${__GSVI_REMATCH_POS_TYPE_EXT_VER}]^^}'
                    eval $__ARG_GSVI_EXTENDED_VERSION='${BASH_REMATCH[${__GSVI_REMATCH_POS_TYPE_VAL_EXT_VER}]}'
                fi
            else
                return 1
            fi
        else
            eval $__ARG_GSVI_MAJOR_VERSION='${__GSVI_TEMP_MJR_VER}'
            eval $__ARG_GSVI_MINOR_VERSION='${__GSVI_TEMP_MNR_VER}'
            eval $__ARG_GSVI_TYPE_EXTENDED_VERSION="RELEASE"
            eval $__ARG_GSVI_EXTENDED_VERSION="0"
        fi
    else
        return 1
    fi

    return 0
}

function set_name_for_tz_entity()
{
    if [ "$TZ_PROVIDER" == "MOBICORE" ] && [ "$MC_DRIVER" == "TRUE" ]; then
        TZ_ENTITY="_dr"
    elif [ "$TZ_PROVIDER" == "TEEGRIS" ] && [ "$TEEGRIS_SYS" == "TRUE" ]; then
        TZ_ENTITY="_sys"

        if [ "$TEEGRIS_SYS_DEBUG" == "TRUE" ]; then
            TZ_ENTITY="${TZ_ENTITY}_dbg"
        fi
    else
        TZ_ENTITY=""
    fi
}

# prepare name of library
#
# arguments:
# $$    internal argument name              type    description
# ----------------------------------------------------------------------------------
# $1    __ARG_PLNE_LIBRARY_VER              INPUT   version string
# $2    __ARG_PLNE_LIBRARY_CL               INPUT   current CL of library
# $3    __ARG_PLNE_RIGHT_LIBRARY_PATH       OUTPUT  general library name
# $4    __ARG_PLNE_ALT_RIGHT_LIBRARY_PATH   OUTPUT  alternative library name
# $5    __ARG_PLNE_LIBRARY_SUFFIX           OUTPUT  suffix of library name
# ---------------------------------------------------------------------------------------------------------------------------------
# FIPS_SCRYPTO_MODULE_VERSION_STR  Binaries naming (default / debug mode)                 Stage           Mode (eng / build-server)
# ---------------------------------------------------------------------------------------------------------------------------------
# SCrypto 2.4.1                    scrypto_v2.4.1.12345678_x32_teegris_sys.so             Development     build-server
#                                  scrypto_v2.4.1.12345678_x32_teegris_sys_debug.so
# ---------------------------------------------------------------------------------------------------------------------------------
# SCrypto 2.4.2                    scrypto_v2.4.2.eng_x32_teegris_sys.so                  Development     eng
#                                  scrypto_v2.4.2.eng_x32_teegris_sys_debug.so
# ---------------------------------------------------------------------------------------------------------------------------------
# SCrypto 2.5.beta.1               scrypto_v2.5.beta.1.12345678_x32_teegris_sys.so        Beta 1          build-server
#                                  scrypto_v2.5.beta.1.12345678_x32_teegris_sys_debug.so
# ---------------------------------------------------------------------------------------------------------------------------------
# SCrypto 2.5.beta.2               scrypto_v2.5.beta.2.eng_x32_teegris_sys.so             Beta 2          eng
#                                  scrypto_v2.5.beta.2.eng_x32_teegris_sys_debug.so
# ---------------------------------------------------------------------------------------------------------------------------------
# SCrypto 2.5.RC.1                 scrypto_v2.5.rc.1.12345678_x32_teegris_sys.so          RC 1            build-server
#                                  scrypto_v2.5.rc.1.12345678_x32_teegris_sys_debug.so
# ---------------------------------------------------------------------------------------------------------------------------------
# SCrypto 2.5.RC.2                 scrypto_v2.5.rc.2.eng_x32_teegris_sys.so               RC 2            eng
#                                  scrypto_v2.5.rc.2.eng_x32_teegris_sys_debug.so
# ---------------------------------------------------------------------------------------------------------------------------------
# SCrypto 2.5                      scrypto_v2.5_x32_teegris_sys_release.so                Release         build-server
#                                  scrypto_v2.5_x32_teegris_sys_debug.so
# ---------------------------------------------------------------------------------------------------------------------------------
# SCrypto 2.5                      scrypto_v2.5.eng_x32_teegris_sys_release.so            Release         eng
#                                  scrypto_v2.5.eng_x32_teegris_sys_debug.so
# ---------------------------------------------------------------------------------------------------------------------------------
# SCrypto 2.5.SR.1                 scrypto_v2.5.sr.1_x32_teegris_sys_release.so           Release (SR 1)  build-server
#                                  scrypto_v2.5.sr.1_x32_teegris_sys_debug.so
# ---------------------------------------------------------------------------------------------------------------------------------
# SCrypto 2.5.SR.2                 scrypto_v2.5.sr.2.eng_x32_teegris_sys_release.so       Release (SR 2)  eng
#                                  scrypto_v2.5.sr.2.eng_x32_teegris_sys_debug.so
# ---------------------------------------------------------------------------------------------------------------------------------
function prepare_library_name()
{
    local __ARG_PLNE_LIBRARY_VER=$1
    local __ARG_PLNE_LIBRARY_CL=$2
    local __ARG_PLNE_RIGHT_LIBRARY_PATH=$3
    local __ARG_PLNE_ALT_RIGHT_LIBRARY_PATH=$4
    local __ARG_PLNE_LIBRARY_SUFFIX=$5

    local __PLNE_MAJOR_VERSION=""
    local __PLNE_MINOR_VERSION=""
    local __PLNE_EXTENDED_VERSION=""
    local __PLNE_TYPE_EXTENDED_VERSION=""

    if ! get_scrypto_version_info ${__ARG_PLNE_LIBRARY_VER} __PLNE_MAJOR_VERSION __PLNE_MINOR_VERSION __PLNE_EXTENDED_VERSION __PLNE_TYPE_EXTENDED_VERSION; then
        print_info "Incorrect version string of SCrypto: '${__ARG_PLNE_LIBRARY_VER}'"
        print_error "Exit...!!!"
    fi

    local __PLNE_CONSTRUCT_LIBRARY_VER="${__PLNE_MAJOR_VERSION}.${__PLNE_MINOR_VERSION}"
    local __PLNE_ALT_CONSTRUCT_LIBRARY_VER=""
    local __PLNE_CONSTRUCT_MODE=""

    local __PLNE_LIBRARY_EXT="a"
    local __PLNE_ALT_LIBRARY_EXT="a"
    local __PLNE_COMMON_LIBRARY_SUFFIX=""
    local __PLNE_LIBRARY_SUFFIX=""
    local __PLNE_ALT_LIBRARY_SUFFIX=""

    set_name_for_tz_entity

    if [ "$TZ_PROVIDER" == "LINUX" ]; then
        __PLNE_LIBRARY_EXT="so"
        __PLNE_ALT_LIBRARY_EXT="so"
    fi

    if [ "$TZ_PROVIDER" == "TEEGRIS" ] && [ "$TEEGRIS_SYS" == "TRUE" ]; then
        __PLNE_ALT_LIBRARY_EXT="so"
    fi

    if [ "${__PLNE_TYPE_EXTENDED_VERSION}" != "RELEASE" ]; then
        if [ "${__PLNE_TYPE_EXTENDED_VERSION}" == "DEV" ]; then
            __PLNE_CONSTRUCT_LIBRARY_VER="${__PLNE_CONSTRUCT_LIBRARY_VER}.${__PLNE_EXTENDED_VERSION,,}"
        else
            __PLNE_CONSTRUCT_LIBRARY_VER="${__PLNE_CONSTRUCT_LIBRARY_VER}.${__PLNE_TYPE_EXTENDED_VERSION,,}.${__PLNE_EXTENDED_VERSION,,}"
        fi
    fi

    __PLNE_ALT_CONSTRUCT_LIBRARY_VER="${__PLNE_CONSTRUCT_LIBRARY_VER}"

    if [ "${IS_ENGINEERING_BUILD}" == "TRUE" ]; then
        __PLNE_CONSTRUCT_LIBRARY_VER="${__PLNE_CONSTRUCT_LIBRARY_VER}.eng"

        #alt name...
        if [ "${__PLNE_TYPE_EXTENDED_VERSION}" == "DEV" ] ||
           [ "${__PLNE_TYPE_EXTENDED_VERSION}" == "BETA" ] ||
           [ "${__PLNE_TYPE_EXTENDED_VERSION}" == "RC" ]; then
            # regex for find utility:
            # $ find ./libs -type f -regex ".*scrypto_v2.4.1.[0-9].*_x32_kinibi.a"
            __PLNE_ALT_CONSTRUCT_LIBRARY_VER="${__PLNE_ALT_CONSTRUCT_LIBRARY_VER}.[0-9].*"
        else
            __PLNE_ALT_CONSTRUCT_LIBRARY_VER="${__PLNE_ALT_CONSTRUCT_LIBRARY_VER}"
        fi

    else
        if [ "${__PLNE_TYPE_EXTENDED_VERSION}" == "DEV" ] ||
           [ "${__PLNE_TYPE_EXTENDED_VERSION}" == "BETA" ] ||
           [ "${__PLNE_TYPE_EXTENDED_VERSION}" == "RC" ]; then
            __PLNE_CONSTRUCT_LIBRARY_VER="${__PLNE_CONSTRUCT_LIBRARY_VER}.${__ARG_PLNE_LIBRARY_CL}"
            __PLNE_ALT_CONSTRUCT_LIBRARY_VER="${__PLNE_ALT_CONSTRUCT_LIBRARY_VER}.[0-9].*"
        fi
    fi

    if [ "${MODE}" == "DEBUG" ]; then
        __PLNE_CONSTRUCT_MODE="_debug"
    elif [ "${__PLNE_TYPE_EXTENDED_VERSION}" == "RELEASE" ] || [ "${__PLNE_TYPE_EXTENDED_VERSION}" == "SR" ]; then
        __PLNE_CONSTRUCT_MODE="_release"
    fi

    local __PLNE_LIBRARY_PATH_WITH_RIGHT_NAME=""
    local __PLNE_ALT_LIBRARY_PATH_WITH_RIGHT_NAME=""
    if [ "${USED_NAMING_LIBRARY_WITH_CHIPSET_TAG}" == "TRUE" ]; then
        __PLNE_COMMON_LIBRARY_SUFFIX="_x${MACHINE}${CRYPTO_CONFIG_SUFFIX}${__PLNE_CONSTRUCT_MODE,,}"
        __PLNE_LIBRARY_SUFFIX="${__PLNE_COMMON_LIBRARY_SUFFIX}.${__PLNE_LIBRARY_EXT}"
        __PLNE_ALT_LIBRARY_SUFFIX="${__PLNE_COMMON_LIBRARY_SUFFIX}.${__PLNE_ALT_LIBRARY_EXT}"
        __PLNE_LIBRARY_PATH_WITH_RIGHT_NAME="${NAME_LIBRARY_TAG,,}${TZ_ENTITY}_${CHIPSET,,}_scrypto_v${__PLNE_CONSTRUCT_LIBRARY_VER}${__PLNE_LIBRARY_SUFFIX}"
        __PLNE_ALT_LIBRARY_PATH_WITH_RIGHT_NAME=".*${NAME_LIBRARY_TAG,,}${TZ_ENTITY}_${CHIPSET,,}_scrypto_v${__PLNE_ALT_CONSTRUCT_LIBRARY_VER}${__PLNE_ALT_LIBRARY_SUFFIX}"
    else
        __PLNE_COMMON_LIBRARY_SUFFIX="_x${MACHINE}_${NAME_LIBRARY_TAG,,}${TZ_ENTITY}${CRYPTO_CONFIG_SUFFIX}${__PLNE_CONSTRUCT_MODE,,}"
        __PLNE_LIBRARY_SUFFIX="${__PLNE_COMMON_LIBRARY_SUFFIX}.${__PLNE_LIBRARY_EXT}"
        __PLNE_ALT_LIBRARY_SUFFIX="${__PLNE_COMMON_LIBRARY_SUFFIX}.${__PLNE_ALT_LIBRARY_EXT}"
        __PLNE_LIBRARY_PATH_WITH_RIGHT_NAME="scrypto_v${__PLNE_CONSTRUCT_LIBRARY_VER}${__PLNE_LIBRARY_SUFFIX}"
        __PLNE_ALT_LIBRARY_PATH_WITH_RIGHT_NAME=".*scrypto_v${__PLNE_ALT_CONSTRUCT_LIBRARY_VER}${__PLNE_ALT_LIBRARY_SUFFIX}"
    fi

    [[ -z "${__ARG_PLNE_RIGHT_LIBRARY_PATH}" ]]     || eval $__ARG_PLNE_RIGHT_LIBRARY_PATH='${__PLNE_LIBRARY_PATH_WITH_RIGHT_NAME}'
    [[ -z "${__ARG_PLNE_ALT_RIGHT_LIBRARY_PATH}" ]] || eval $__ARG_PLNE_ALT_RIGHT_LIBRARY_PATH='${__PLNE_ALT_LIBRARY_PATH_WITH_RIGHT_NAME}'
    [[ -z "${__ARG_PLNE_LIBRARY_SUFFIX}" ]]         || eval $__ARG_PLNE_LIBRARY_SUFFIX='${__PLNE_LIBRARY_SUFFIX}'
}

# getting scrypto version from source code (/openssl/scrypto_version.h)
#
# arguments:
# $$    internal argument name              type    description
# ----------------------------------------------------------------------------------
# $1    __ARG_SVFS_LIBRARY_VER              OUTPUT  version string
# ----------------------------------------------------------------------------------
function get_scrypto_version_from_source_code()
{
    local __ARG_SVFS_LIBRARY_VER=$1
    local __SVFS_LOCAL_DIR=$(dirname "$(readlink -f "$BASH_SOURCE")")
    local __SVFS_VER_HEADER_PATH="$__SVFS_LOCAL_DIR/../src/include/openssl/scrypto_version.h"
    local __SVFS_LIBRARY_VER_SOURCE=""

    if [ ! -f "$__SVFS_VER_HEADER_PATH" ]; then
        return 1
    fi

    __SVFS_LIBRARY_VER_SOURCE=$(grep FIPS_SCRYPTO_MODULE_VERSION_STR "$__SVFS_VER_HEADER_PATH" | tr  -d '"' | awk '{print $4}')

    if [ -z "$__SVFS_LIBRARY_VER_SOURCE" ]; then
        return 1
    fi

    eval $__ARG_SVFS_LIBRARY_VER='${__SVFS_LIBRARY_VER_SOURCE}'

    return 0
}

# store scrypto version to version file (version_info)
#
# arguments:
# $$    internal argument name              type    description
# ----------------------------------------------------------------------------------
# $1    __ARG_SVVF_LIBRARY_VER              INPUT   version string
# ----------------------------------------------------------------------------------
function strore_scrypto_version_to_version_file()
{
    local __ARG_SVVF_LIBRARY_VER=$1
    local __SVVF_LOCAL_DIR=$(dirname "$(readlink -f "$BASH_SOURCE")")
    local __SVVF_VERSION_FILE="$__SVVF_LOCAL_DIR/version_info"

    echo "#!/bin/bash" > "$__SVVF_VERSION_FILE"
    echo "# This file has been automatically generated." >> "$__SVVF_VERSION_FILE"
    echo "# Do not modify." >> "$__SVVF_VERSION_FILE"
    echo "export VERSION=\"$__ARG_SVVF_LIBRARY_VER\"" >> "$__SVVF_VERSION_FILE"
}

# getting parts (general name, alternative name and suffix) of library name
#
# arguments:
# $$    internal argument name              type    description
# ----------------------------------------------------------------------------------
# $1    __ARG_GPLN_LIBRARY_PATH             OUTPUT  general library name
# $2    __ARG_GPLN_ALT_LIBRARY_PATH         OUTPUT  alternative library name
# $3    __ARG_GPLN_LIBRARY_SUFFIX           OUTPUT  suffix of library name
# ----------------------------------------------------------------------------------
function get_parts_of_library_name()
{
    local __ARG_GPLN_LIBRARY_PATH=$1
    local __ARG_GPLN_ALT_LIBRARY_PATH=$2
    local __ARG_GPLN_LIBRARY_SUFFIX=$3

    local __GPLN_LIBRARY_VER=""
    local __GPLN_LIBRARY_CL=""

    local __GPLN_LIBRARY_PATH=""
    local __GPLN_ALT_LIBRARY_PATH=""
    local __GPLN_LIBRARY_SUFFIX=""

    get_scrypto_version_from_source_code __GPLN_LIBRARY_VER
    if ! get_current_cl_number __GPLN_LIBRARY_CL; then
        return 1
    fi

    prepare_library_name "${__GPLN_LIBRARY_VER}" "${__GPLN_LIBRARY_CL}" __GPLN_LIBRARY_PATH __GPLN_ALT_LIBRARY_PATH __GPLN_LIBRARY_SUFFIX

    [[ -z "${__ARG_GPLN_LIBRARY_PATH}" ]]       || eval $__ARG_GPLN_LIBRARY_PATH='${__GPLN_LIBRARY_PATH}'
    [[ -z "${__ARG_GPLN_ALT_LIBRARY_PATH}" ]]   || eval $__ARG_GPLN_ALT_LIBRARY_PATH='${__GPLN_ALT_LIBRARY_PATH}'
    [[ -z "${__ARG_GPLN_LIBRARY_SUFFIX}" ]]     || eval $__ARG_GPLN_LIBRARY_SUFFIX='${__GPLN_LIBRARY_SUFFIX}'

    return 0
}

# getting right library name
#
# arguments:
# $$    internal argument name              type    description
# ----------------------------------------------------------------------------------
# $1    __ARG_GRLN_RIGHT_LIBRARY_NAME       OUTPUT  right library name
# ----------------------------------------------------------------------------------
function get_right_library_name()
{
    local __ARG_GRLN_RIGHT_LIBRARY_NAME=$1
    local __GRLN_LIBRARY_VER=""
    local __GRLN_LIBRARY_CL=""
    local __GRLN_LIBRARY_PATH=""
    local __GRLN_BASENAME_LIBRARY_PATH=""

    get_scrypto_version_from_source_code __GRLN_LIBRARY_VER
    strore_scrypto_version_to_version_file ${__GRLN_LIBRARY_VER}
    if ! get_parts_of_library_name __GRLN_LIBRARY_PATH; then
        return 1
    fi

    __GRLN_BASENAME_LIBRARY_PATH=$(basename "${__GRLN_LIBRARY_PATH}")

    eval $__ARG_GRLN_RIGHT_LIBRARY_NAME='${__GRLN_BASENAME_LIBRARY_PATH}'

    return 0
}

function prepare_toolchains()
{
    if [ "$(echo "$ARM_RVCT_PATH" | grep '/opt/toolchains/ARM-DS5/6')" != "" ] ; then
        # If DS-5 V6 compiler is used
        rm -rf build_links
        mkdir -p build_links/bin

        ln -sf "${ARM_RVCT_PATH}"/bin/* "$(pwd)/build_links/bin/"
        export PATH=$PATH:${ARM_RVCT_PATH}/bin
        ln -sf "${TOOLS_DIR}"/build_env/* "$(pwd)/build_links/bin/"
        export ARM_RVCT_PATH_BIN=""
        ARM_RVCT_PATH_BIN=$(pwd)/build_links/bin/

        print_info "ARM_RVCT_PATH_BIN=$ARM_RVCT_PATH_BIN"
    fi
}

function is_library_with_asm()
{
    [ -e "$1" ] || print_error "The \"$1\" file doesn't exit!"
    return $(ar -t "$1" | grep -qe ".*S\.o$")
}

function is_library_with_custom_libc()
{
    [ -e "$1" ] || print_error "The \"$1\" file doesn't exit!"
    return $(ar -t "$1" | grep -qe "CUSTOM_LIBC_")
}

function is_library_with_no_neon()
{
    return $([[ "$EXTERNAL_LIB_MODULE_CONFIG" == *"module_config_armv4_no_neon"* ]])
}

function get_right_syms_check_list()
{
    function add_config_suffix()
    {
        # Ex:
        # 1) config/additional/crypto_config_generic_asm -> _[generic_asm]
        # 2) ./config/CI/debug_mem_trace_module_config -> ""
        # 3) samples/RSA_Sign_Sample -> _[samples/RSA_Sign_Sample]
        SUFFIX=$(basename "$1" | sed 's/.*_config[_]\?\(.*\)/\1/g')
        if [ "$SUFFIX" != "" ] ; then
            SUFFIX="_[$SUFFIX]"
        fi
        echo $SUFFIX
    }

    set_name_for_tz_entity

    local SYMS_LIST="${TOOLS_DIR}/FIPSCANISTER_syms_check/SWD_FIPSCANISTER_syms_reference_${ROOT_TEE}${TZ_ENTITY}_${CHIPSET}_x${MACHINE}"

    if [ $TEE = "tg" ]; then
        SYMS_LIST="${TOOLS_DIR}/FIPSCANISTER_syms_check/SWD_FIPSCANISTER_syms_reference_${ROOT_TEE}${TEE_VERSION}${TZ_ENTITY}_${CHIPSET}_x${MACHINE}"
    fi

    is_library_with_custom_libc "${LIBCRYPTO_LIB_PATH}" \
    && SYMS_LIST="${SYMS_LIST}_[custom_libc]" || true
    is_library_with_asm "$LIBCRYPTO_LIB_PATH" \
    || SYMS_LIST="${SYMS_LIST}_[no_asm]"

    SYMS_LIST="${SYMS_LIST}$(add_config_suffix $EXTERNAL_LIB_MODULE_CONFIG)"
    SYMS_LIST="${SYMS_LIST}$(add_config_suffix $EXTERNAL_LIB_CRYPTO_CONFIG)"
    SYMS_LIST="${SYMS_LIST}$(add_config_suffix $EXTERNAL_LIB_USER_CONFIG)"
    SYMS_LIST="${SYMS_LIST}.list"
    readlink -f ${SYMS_LIST}
}

function check_adb_device()
{
    function more_than_one_device_error()
    {
        print_error "ERROR: \n
        Multiple devices connected but any single device specified. \n
        Export \$adb_device variable to specify a single device serial number."
        adb devices -l
    }

    echo ""
    local DEV_LIST="$(adb devices -l | sed '/^$/d' | grep -ve '* .* *' | grep -v 'List of devices attached')"
    if [ -z "${adb_device}" ]; then
      local DEV_NUM="$(adb devices -l | grep -c ' device ')"
      test "$DEV_NUM" -eq 0 && print_error "List of devices is empty" && return 1
      test "$DEV_NUM" == 1 && export adb_device="-s $(echo ${DEV_LIST} | awk '{print $1}' | tr -d '\n\r')"
      test "$DEV_NUM" -gt 1 && more_than_one_device_error && return 1
    else
      echo ${DEV_LIST} | grep -w ${adb_device} > /dev/null 2>&1
      if [ "$?" != '0' ]; then
        print_error "DEVICE: <${adb_device##-s}> is not present"
        adb devices -l
        return 1
      fi
    fi
    return 0
}

function adb_device_reboot()
{
    adb ${adb_device} reboot
    return $?
}

function perforce_preconditions_check()
{
    print_info "P4 is ${P4}"
    print_info "P4PORT is ${P4PORT}"
    print_info "P4USER is ${P4USER}"
    print_info "P4CLIENT is ${P4CLIENT}"
    print_info "WORKDIR is ${WORKDIR}"

    if [[ -z "${P4}" || -z "${P4CLIENT}" \
        || -z "${P4USER}" || -z "${P4PORT}" ]]; then
      print_warning "==> Error! One of the mandatory options is empty!">&2
      return 1
    fi
    if [[ -z "${P4PASSWD}" ]]; then
      print_warning "==> Error! \${P4PASSWD} is not set!">&2
      return 1
    fi
    return 0
}

# getting current CL number
#
# arguments:
# $$    internal argument name              type    description
# ----------------------------------------------------------------------------------
# $1    __ARG_GCCN_CURRENT_CL               OUTPUT  current CL of library
# ----------------------------------------------------------------------------------
function get_current_cl_number()
{
    local __ARG_GCCN_CURRENT_CL=$1
    local __GCCN_LOCAL_DIR=$(dirname "$(readlink -f "$BASH_SOURCE")")
    local __GCCN_TMP_INT_VER_FILE="${__GCCN_LOCAL_DIR}/../internal_version.txt"
    local __GCCN_VALUE_INTERNAL_VERSION=""

    # default return value
    eval $__ARG_GCCN_CURRENT_CL=""

    if [ "$IS_ENGINEERING_BUILD" == "FALSE" ]; then
        if [ "$IS_EMERGENCY_RELEASE_BUILD" == "TRUE" ]; then
            perforce_preconditions_check || return 1
        fi

        ${TOOLS_DIR}/get_build_info.sh "${__GCCN_TMP_INT_VER_FILE}" "$P4" "$P4CLIENT" "$P4PORT" "$P4USER" "$P4PASSWD" || return 1
        __GCCN_VALUE_INTERNAL_VERSION=$(grep -m 1 -e '^Change ' ${__GCCN_TMP_INT_VER_FILE} | awk '{print $2}')
        rm ${__GCCN_TMP_INT_VER_FILE}

        if [ "${__GCCN_VALUE_INTERNAL_VERSION}" == "" ]; then
            print_warning "Could not get value of internal version from current CL."
            return 1
        fi

        eval $__ARG_GCCN_CURRENT_CL='${__GCCN_VALUE_INTERNAL_VERSION}'
    fi

    return 0
}

# create internal version info
#
# arguments:
# $$    internal argument name              type    description
# ----------------------------------------------------------------------------------
# $1    __ARG_CIVI_INTERNAL_VERSION_INFO    OUTPUT  internal version info
# ----------------------------------------------------------------------------------
function create_internal_version_info()
{
    local __ARG_CIVI_INTERNAL_VERSION_INFO=$1
    local __CIVI_VALUE_INTERNAL_VERSION=""

    if [ "$IS_ENGINEERING_BUILD" == "TRUE" ]; then
        __CIVI_VALUE_INTERNAL_VERSION="SCRYPTO_ENGINEERING_BUILD: "

        if [ "$SCRYPTO_DEBUG" == "TRUE" ]; then
            __CIVI_VALUE_INTERNAL_VERSION+="[DEBUG]"
        else
            __CIVI_VALUE_INTERNAL_VERSION+="[RELEASE]"
        fi
    else
        if ! get_current_cl_number __CIVI_VALUE_INTERNAL_VERSION; then
            return 1
        fi
    fi

    __CIVI_VALUE_INTERNAL_VERSION="CL ${__CIVI_VALUE_INTERNAL_VERSION}"
    eval $__ARG_CIVI_INTERNAL_VERSION_INFO='\"${__CIVI_VALUE_INTERNAL_VERSION}\"'

    return 0
}

# create numerical version info
#
# arguments:
# $$    internal argument name               type    description
# ----------------------------------------------------------------------------------
# $1    __ARG_CNVI_NUMERICAL_VERSION_INFO    OUTPUT  numerical version info
# ----------------------------------------------------------------------------------
# FIPS_SCRYPTO_MODULE_VERSION_STR    FIPS_SCRYPTO_MODULE_VERSION_NUM
# ----------------------------------------------------------------------------------
# SCrypto 2.4.1                      0x0204DE01
# ----------------------------------------------------------------------------------
# SCrypto 2.4.2                      0x0204DE02
# ----------------------------------------------------------------------------------
# SCrypto 2.5.beta.1                 0x02052001
# ----------------------------------------------------------------------------------
# SCrypto 2.5.beta.2                 0x02052002
# ----------------------------------------------------------------------------------
# SCrypto 2.5.RC.1                   0x02054001
# ----------------------------------------------------------------------------------
# SCrypto 2.5.RC.2                   0x02054002
# ----------------------------------------------------------------------------------
# SCrypto 2.5                        0x02056000
# ----------------------------------------------------------------------------------
# SCrypto 2.5.SR.1                   0x02058001
# ----------------------------------------------------------------------------------
# SCrypto 2.5.SR.2                   0x02058002
# ----------------------------------------------------------------------------------
# SCrypto 2.5.1                      0x0205DE01
# ----------------------------------------------------------------------------------
function create_numerical_version_info() {
    local __ARG_CNVI_NUMERICAL_VERSION_INFO=$1

    local __CNVI_LIBRARY_VER=""
    local __CNVI_MJR=""
    local __CNVI_MNR=""
    local __CNVI_EXT=""
    local __CNVI_TYP=""
    local __CNVI_TYP_STR=""
    local __CNVI_LIBRARY_NUM_VER=""

    get_scrypto_version_from_source_code __CNVI_LIBRARY_VER

    if ! get_scrypto_version_info ${__CNVI_LIBRARY_VER} __CNVI_MJR __CNVI_MNR __CNVI_EXT __CNVI_TYP_STR; then
        print_info "Incorrect version string of SCrypto: '${__CNVI_LIBRARY_VER}'"
        print_error "Exit...!!!"
    fi

    case "$__CNVI_TYP_STR" in
        "BETA")
            __CNVI_TYP="0x20";;
        "RC")
            __CNVI_TYP="0x40";;
        "RELEASE")
            __CNVI_TYP="0x60";;
        "SR")
            __CNVI_TYP="0x80";;
        "DEV")
            __CNVI_TYP="0xDE";;
        *)
            print_info "Incorrect type of extended version of SCrypto: '${__CNVI_TYP_STR}'"
            print_error "Exit...!!!"
    esac

    ((__CNVI_LIBRARY_NUM_VER = __CNVI_MJR*0x01000000 + __CNVI_MNR*0x00010000 + __CNVI_TYP*0x00000100 + __CNVI_EXT))
    eval $__ARG_CNVI_NUMERICAL_VERSION_INFO='$(printf "0x%08X\n" $__CNVI_LIBRARY_NUM_VER)'

    return 0
}

function update_property_in_version_header()
{
    set +e
    local __ARG_PROPERTY_NAME="$1"
    local __ARG_PROPERTY_VALUE="$2"

    local LOCAL_DIR=$(dirname "$(readlink -f "$BASH_SOURCE")")
    local VERSION_HEADER="${LOCAL_DIR}/../src/include/openssl/scrypto_version.h"

    echo "Property ${__ARG_PROPERTY_NAME}"

    if [ "$(grep -oP ${__ARG_PROPERTY_NAME}' \K.*' ${VERSION_HEADER})" != "${__ARG_PROPERTY_VALUE}" ]; then
        echo "Update value..."
        sed -i "s/\(${__ARG_PROPERTY_NAME} \).*/\1${__ARG_PROPERTY_VALUE}/" "${VERSION_HEADER}"
    fi

    echo "-- current value: ${__ARG_PROPERTY_VALUE}"
    set -e
    return 0
}

function update_version_header()
{
    set +e
    local INTERNAL_VERSION_INFO=""
    if ! create_internal_version_info INTERNAL_VERSION_INFO; then
        return 1
    fi
    update_property_in_version_header "FIPS_SCRYPTO_INTERNAL_VERSION_STR" "$INTERNAL_VERSION_INFO"

    local NUMERICAL_VERSION_INFO=""
    if ! create_numerical_version_info NUMERICAL_VERSION_INFO; then
        return 1
    fi
    update_property_in_version_header "FIPS_SCRYPTO_MODULE_VERSION_NUM" "$NUMERICAL_VERSION_INFO"
    set -e
    return 0
}

# Function for checking changes in binary library between previous and current build
# Performed a binary comparison excluding information about the internal version of the library.
function check_for_changed_build()
{
    [[ "$IS_ENGINEERING_BUILD" == "TRUE" || "$IS_EMERGENCY_RELEASE_BUILD" == "TRUE" ]] && return 0

    local LOCAL_DIR=$(dirname "$(readlink -f "$BASH_SOURCE")")
    local ROOT_DIR="${LOCAL_DIR}/.."
    local OBJECT_SCRYPTO_VERSION="scrypto_version.c.o"
    local FLAG_FOR_DETERMINISTIC_ARCH="--enable-deterministic-archives"

    local CURRENT_LIB_NAME=""
    local CURRENT_LIB_SUFFIX=""

    if ! get_right_library_name CURRENT_LIB_NAME; then
        print_error "Failed to get library name value"
    fi

    if ! get_parts_of_library_name "" "" CURRENT_LIB_SUFFIX; then
        print_error "Failed to get library name suffix value"
    fi

    local PATH_CURRENT_LIB="$(find -L "${ROOT_DIR}/samples/scrypto" -name "${CURRENT_LIB_NAME}")"
    local PATH_PREVIOUS_LIB_COMPARE="$(find -L "${ROOT_DIR}/${RESULT_CHANGED_BUILD_PATH_DIR}" -name "*${CURRENT_LIB_SUFFIX}")"
    local PATH_CURRENT_LIB_COMPARE="${ROOT_DIR}/${RESULT_CHANGED_BUILD_PATH_DIR}/${CURRENT_LIB_NAME}.new.a"
    local PATH_RESULT_FILE="${ROOT_DIR}/${RESULT_CHANGED_BUILD_PATH_FILE}"
    local IS_CHANGES_BUILD="CHANGED"

    set +e

    [[ ! -e "${PATH_RESULT_FILE}" ]] && mkdir -p "${ROOT_DIR}/${RESULT_CHANGED_BUILD_PATH_DIR}" && touch "${PATH_RESULT_FILE}"

    # check for the existence of binary from previous build
    if [ -e "${PATH_PREVIOUS_LIB_COMPARE}" ]; then
        # copy current binary in directory for comparison
        chmod 0666 ${PATH_PREVIOUS_LIB_COMPARE}
        cp -f "${PATH_CURRENT_LIB}" "${PATH_CURRENT_LIB_COMPARE}"

        # remove members with version info from binary (previous and current)
        "${AR}" "${AR_O_DELETEMEMBERS}" "${PATH_CURRENT_LIB_COMPARE}" "${OBJECT_SCRYPTO_VERSION}"
        "${AR}" "${AR_O_DELETEMEMBERS}" "${PATH_PREVIOUS_LIB_COMPARE}" "${OBJECT_SCRYPTO_VERSION}"

        # strip both binary to remove additional archive info
        "${STRIP}" "${FLAG_FOR_DETERMINISTIC_ARCH}" "${PATH_CURRENT_LIB_COMPARE}"
        "${STRIP}" "${FLAG_FOR_DETERMINISTIC_ARCH}" "${PATH_PREVIOUS_LIB_COMPARE}"

        # compare previous and current binary
        # if the binaries are the same then mark as "not changed"
        if [ -z "$(diff "${PATH_CURRENT_LIB_COMPARE}" "${PATH_PREVIOUS_LIB_COMPARE}")" ]; then
            IS_CHANGES_BUILD="unchanged"
        fi
    fi

    # save result of comparison in the report file
    echo "${CURRENT_LIB_NAME} ${IS_CHANGES_BUILD}" >> "${PATH_RESULT_FILE}"

    set -e
    print_info "Built library ${CURRENT_LIB_NAME} is ${IS_CHANGES_BUILD}."
}

function get_device_chipname()
{
    adb ${adb_device} shell getprop | grep chipname | cut -d ':' -f2 | tr -d '[:space:]\[\]' | tr '[:upper:]' '[:lower:]'
}

function get_remount_device_point_qc()
{
    local res=
    local chipname="$(get_device_chipname)"

    if [ "${chipname}" == "sm8150" ] || [ "${chipname}" == "sm8250" ]; then
        res="/vendor/firmware_mnt"
    else
        #default value for QC
        res="/firmware"
    fi

    echo "$res"
}

function get_remount_device_point_mc()
{
    #default value for MC
    local res="/vendor"

    echo "$res"
}

function get_remount_device_point_tg()
{
    local res=
    local destination="${1}"

    if [ "${destination}" == "device" ]; then
        res="/vendor"
    elif [ "${destination}" == "qemu" ]; then
        #default value for TG (qemu)
        res="/vendor"
    else
        print_error "Remount device point on Teegris platform: Unknown destination value '${destination}'"
    fi

    echo "$res"
}

function get_remount_device_point()
{
    local platform="${1}"
    local destination="${2}"

    echo "$(get_remount_device_point_${platform} ${destination})"
}

function check_golang()
{
    local LOCAL_DIR=$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")
    local go_mod_file="${LOCAL_DIR}"/../src/go.mod
    local go_bin=
    local req_ver_go=
    local crr_ver_go=

    if [ ! -f "$GO_EXECUTABLE" ]; then
        print_warning "'$GO_EXECUTABLE' not found. Will use system Go"
        go_bin=$(command -v go) \
        || (print_error "Go not found.\
                         \\n  Please use the most recent stable version of Go.\
                         \\n  See src/BUILDING.md: Build Prerequisites";
            exit 1)
        export GO_EXECUTABLE="$go_bin"
    fi

    if [ "$TZ_PROVIDER" != "LINUX" ] || [ "$NO_GOLANG_VERSION_CHECK" == "TRUE" ]; then
        return 0
    fi

    if [ ! -f "$go_mod_file" ]; then
        print_warning "src/go.mod not found. Can not specify required version of Go"
        return 0
    fi
    # Structure of src/go.mod:
    # <modules>
    # go <X.Y>
    req_ver_go=$(grep -e "go\\s[0-9]" "$go_mod_file" | cut -d' ' -f2)
    print_info "Required Go $req_ver_go"

    # Example of command 'go version' out:
    # $ go version go1.2.1 linux/amd64
    crr_ver_go=$("${GO_EXECUTABLE}" version | cut -d' ' -f3 | cut -b3-)
    print_info "Available Go $crr_ver_go"

    if [ "$(printf '%s\n' "$req_ver_go" "$crr_ver_go" | sort -V | head -n1)" != "$req_ver_go" ]; then
        print_error "Used low version Go $crr_ver_go.\
                     \\n  Please use Go $req_ver_go or higher.\
                     \\n  See src/BUILDING.md: Build Prerequisites"
        exit 1
    fi
}

# Export functions
export -f get_stack
export -f print_info
export -f print_error
export -f print_success
export -f print_warning
export -f check_return_code
export -f contain_element
export -f check_required
export -f check_existing
export -f source_file
export -f check_arg_num
export -f get_scrypto_version_info
export -f set_name_for_tz_entity
export -f prepare_library_name
export -f get_scrypto_version_from_source_code
export -f strore_scrypto_version_to_version_file
export -f get_parts_of_library_name
export -f get_right_library_name
export -f prepare_toolchains
export -f is_library_with_asm
export -f is_library_with_custom_libc
export -f is_library_with_no_neon
export -f get_right_syms_check_list
export -f check_adb_device
export -f adb_device_reboot
export -f perforce_preconditions_check
export -f get_current_cl_number
export -f create_internal_version_info
export -f create_numerical_version_info
export -f update_version_header
export -f check_for_changed_build
export -f get_device_chipname
export -f get_remount_device_point_qc
export -f get_remount_device_point_mc
export -f get_remount_device_point_tg
export -f get_remount_device_point
export -f check_golang
