#!/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'

# retrieve the number of executing processors
export NCPU="$(getconf _NPROCESSORS_ONLN 2> /dev/null)"

# 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>
function print_warning()
{
    echo -e "${YELLOW}[WARNING]${NC}" "$@"
}

# 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
}

function get_right_library_name
{
    if [ "$TZ_PROVIDER" == "LINUX" ]; then
         local LIBRARY_EXT="so"
    else
        local LIBRARY_EXT="a"
    fi

    if [ "$TZ_PROVIDER" == "MOBICORE" ] && [ "$MC_DRIVER" == "TRUE" ]; then
        TZ_ENTITY="_dr"
    else
        TZ_ENTITY=""
    fi

    local LOCAL_DIR=$(dirname "$(readlink -f "$BASH_SOURCE")")
    local VER_HEADER_PATH="$LOCAL_DIR/../src/include/openssl/base.h"
    VERSION_FILE="$LOCAL_DIR/version_info"

    if [ ! -f "$VER_HEADER_PATH" ]; then
        print_error "Version header file '$VER_HEADER_PATH' doesn't exist"
    fi

    local LIBRARY_VER=$(grep FIPS_SCRYPTO_MODULE_VERSION_STR "$VER_HEADER_PATH" | tr  -d '"' | awk '{print $4}')

    echo "#!/bin/bash" > "$VERSION_FILE"
    echo "# This file has been automatically generated." >> "$VERSION_FILE"
    echo "# Do not modify." >> "$VERSION_FILE"
    echo "export VERSION=\"$LIBRARY_VER\"" >> "$VERSION_FILE"

    if [ -z "$LIBRARY_VER" ]; then
        print_error "Version string in '$VER_HEADER_PATH' is empty"
    fi

    local LIBRARY_PATH_WITH_RIGHT_NAME="${TZ_PROVIDER,,}${TZ_ENTITY}_${CHIPSET,,}_scrypto_v${LIBRARY_VER}_x${MACHINE}_${MODE,,}.${LIBRARY_EXT}"
    basename "$LIBRARY_PATH_WITH_RIGHT_NAME"
}

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
    }
    local SYMS_LIST="${TOOLS_DIR}/FIPSCANISTER_syms_check/SWD_FIPSCANISTER_syms_reference_${TEE}_${CHIPSET}_x${MACHINE}"

    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}
}

# 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_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
