#!/usr/bin/env bash
umask 0027
set -uo pipefail

#------------------------------------------------------------------
# Library - PowerLinux configuration
#------------------------------------------------------------------
# Script name: "lib_platf_power"
#
#------------------------------------------------------------------

# return if library is already loaded
[[ -n "${LIB_PLATF_POWER_RELEASE:-}" ]] && return 0

LIB_PLATF_POWER_RELEASE='loaded'

##########################################################
# Global functions - to be used in other functions
##########################################################
function LIB_FUNC_TRANSFORM_POWER_POWERMODE {

    local powermode="$1"
    shift 1

    logTrace "<${BASH_SOURCE[0]}:${FUNCNAME[*]}>"

    # P7,P8                                             P9,P10
    # 0x0001: "Dynamic, Favor Performance"              0x0001: "Maximum Performance"
    # 0x0002: "None"                                    0x0002: "None"
    # 0x0003: "Static"                                  0x0003: "Static"
    # 0x00ff: "Dynamic, Favor Power"                    0x0004: "Dynamic Performance"
    # default: "Unknown"                                default: "Unknown"

    if [[ "${LIB_PLATF_POWER_PLATFORM_BASE:-}" =~ POWER(10|9) ]]; then

        case "${powermode}" in
            '0001')  : 'Maximum Performance' ;;
            '0002')  : 'None' ;;
            '0003')  : 'Static' ;;
            '0004')  : 'Dynamic Performance' ;;
            *)       : 'Unknown';;
        esac
        powermode="$_"

    elif [[ "${LIB_PLATF_POWER_PLATFORM_BASE:-}" =~ POWER8 ]]; then

        case "${powermode}" in
            '0001')  : 'Dynamic, Favor Performance' ;;
            '0002')  : 'None' ;;
            '0003')  : 'Static' ;;
            '00ff')  : 'Dynamic, Favor Power' ;;
            *)       : 'Unknown';;
        esac
        powermode="$_"

    else

        powermode='Unknown'

    fi

    printf -v RETURN_POWER_POWERMODE '%s' "${powermode}"

}

function LIB_FUNC_IBMCLOUD_POWER_INSTANCE_NAME {

    local prefix
    local -i cores=0
    local -i memory_available_gb=0
    local -i memory_calc_gb=0

    logTrace "<${BASH_SOURCE[0]}:${FUNCNAME[*]}>"

    if [[ -n ${LIB_PLATF_POWER_CPU_TOTAL:-} && -n ${LIB_PLATF_CPU_THREADSPERCORE:-} ]]; then

        cores=$(( LIB_PLATF_POWER_CPU_TOTAL/LIB_PLATF_CPU_THREADSPERCORE ))

    fi

    if [[ -n ${LIB_PLATF_RAM_MiB_AVAILABLE:-} ]]; then

        printf -v memory_available_gb '%.0f' $(( (LIB_PLATF_RAM_MiB_AVAILABLE+1023)/1024 ))

    fi

    if [[ -n ${cores:-} && -n ${memory_available_gb:-} ]]; then

        local -i ratio=$(( memory_available_gb/cores ))

        if   ((230<=ratio && ratio<=250)); then     ratio=240; : "umh"
        elif ((170<=ratio && ratio<=190)); then     ratio=180; : "mh"
        elif (( 90<=ratio && ratio<=110)); then     ratio=100; : "bh"
        elif (( 40<=ratio && ratio<=60 )); then     ratio=50;  : "ch"
        else                                                   : "ush" # remaining
        fi

        prefix="$_"

        memory_calc_gb=$(( cores*ratio ))

    fi

    logTrace "<${BASH_SOURCE[0]}:${FUNCNAME[*]}> # CPUt: ${LIB_PLATF_POWER_CPU_TOTAL:-}; Memory: ${memory_available_gb:-} GB; Ratio: ${ratio:-}"

    #umh1-16x3840 - variable is available to caller
    printf -v RETURN_IBMCLOUD_POWER_INSTANCE_NAME '%s1-%sx%s' "${prefix}" "${cores}" "${memory_calc_gb}"

}

##########################################################
# Non-global functions - not to be used in other scripts
##########################################################
function __lib_func_get_vendor_details {

    logTrace "<${BASH_SOURCE[0]}:${FUNCNAME[*]}>"

    if [[ -x '/opt/rsct/bin/ctgethscid' ]]; then

        while read -r line ; do

            logTrace "<${FUNCNAME[0]}> # ${line}>"

            # PartitionUUID="1234567a-8b90-1c23-4567-89d0ef01a2bc"
            # HscHostName[9]="ibmcloud"

            case ${line} in

                'HscHostName[9]="ibmcloud"')
                            LIB_PLATF_VENDOR='IBM Cloud POWER'
                ;;

            esac

        done <<< "$(/opt/rsct/bin/ctgethscid)"

    fi

    [[ -z "${LIB_PLATF_VENDOR:-}" ]] && LIB_PLATF_VENDOR='IBM'
    readonly LIB_PLATF_VENDOR

}

function __lib_func_get_hypervisor_details {

    #( pHyp - para ) ; ( pHyp - none ) ; ( KVM - para ) ; ( none - none ) = PowerNV = bare-metal

    # https://github.com/karelzak/util-linux/blob/master/sys-utils/lscpu.c
    # also used in HWCCT landscapeTest.py

    logTrace "<${BASH_SOURCE[0]}:${FUNCNAME[*]}>"

    local hyp_v
    local hyp_t

    #PowerVM (IBM's proprietary hypervisor, aka pHyp)
    if [[   -r '/proc/device-tree/ibm,partition-name' && \
            -e '/proc/device-tree/hmc-managed?' && \
            ! -e '/proc/device-tree/chosen/qemu,graphic-width' ]]; then

        hyp_v='pHyp' ; hyp_t='para'

        #use read instead of <(); bash version 4.4 complains if file contains \0
        local partname
        IFS= read -r -d '' -n4 partname </proc/device-tree/ibm,partition-name;
        [[ "${partname}" == 'full' ]] && hyp_t='none'

    elif [[ -r '/proc/device-tree/compatible' ]]; then

        local pdt_compatible
        pdt_compatible=$(</proc/device-tree/compatible)
        readonly pdt_compatible

        case "${pdt_compatible}" in

            'qemu,pseries')
                            hyp_v='KVM' ; hyp_t='para'
            ;;

            'ibm,powernv')  # PowerNV (POWER Non-Virtualized, bare-metal)
                            hyp_v='none' ; hyp_t='none'
            ;;

            *)
                            hyp_v='NULL' ; hyp_t='NULL'
            ;;

        esac

    else
        hyp_v='NULL' ; hyp_t='NULL'
    fi

    LIB_PLATF_VIRT_HYPER=${hyp_v}
    LIB_PLATF_VIRT_TYPE=${hyp_t}
    readonly LIB_PLATF_VIRT_HYPER
    readonly LIB_PLATF_VIRT_TYPE

    logDebug "<${BASH_SOURCE[0]}:${FUNCNAME[0]}> # LIB_PLATF_VIRT_HYPER : ${LIB_PLATF_VIRT_HYPER:-}; LIB_PLATF_VIRT_TYPE ${LIB_PLATF_VIRT_TYPE:-}"

}

function __lib_func_get_lpar_details {

    logTrace "<${BASH_SOURCE[0]}:${FUNCNAME[*]}>"

    #https://manpages.debian.org/testing/powerpc-ibm-utils/lparcfg.5.en.html
    local -r lparcfgfile='/proc/ppc64/lparcfg'

    [[ ! -r ${lparcfgfile} ]] && return

    while read -r line; do

        logTrace "<${FUNCNAME[0]}> # ${lparcfgfile}:${line}"

        case ${line} in

            'system_type'*)
                            #<Model: IBM,9119-MHE>
                            if [[ -z ${LIB_PLATF_NAME:-} ]]; then
                                LIB_PLATF_NAME=$(LIB_FUNC_TRIM_LEFT "${line/system_type=}")
                                readonly LIB_PLATF_NAME
                            fi
            ;;

            'capped'*)
                            if [[ -z ${LIB_PLATF_POWER_CPUCAP:-} ]]; then
                                _temp=$(LIB_FUNC_TRIM_LEFT "${line/capped=}")
                                [[ ${_temp} = 1 ]] && _temp='CAPPED' || _temp='UNCAPPED'
                                LIB_PLATF_POWER_CPUCAP="${_temp}"
                                readonly LIB_PLATF_POWER_CPUCAP
                            fi
            ;;

            'entitled_memory='*)
                            if [[ -z ${LIB_PLATF_POWER_MEMENTITLED:-} ]]; then
                                _temp=$(LIB_FUNC_TRIM_LEFT "${line/entitled_memory=}")
                                LIB_PLATF_POWER_MEMENTITLED="${_temp}"
                                readonly LIB_PLATF_POWER_MEMENTITLED
                            fi
            ;;

            'mapped_entitled_memory='*)
                            if [[ -z ${LIB_PLATF_POWER_MEMENTITLEDMAPPED:-} ]]; then
                                _temp=$(LIB_FUNC_TRIM_LEFT "${line/mapped_entitled_memory=}")
                                LIB_PLATF_POWER_MEMENTITLEDMAPPED="${_temp}"
                                readonly LIB_PLATF_POWER_MEMENTITLEDMAPPED
                            fi
            ;;

            'entitled_memory_weight='*)
                            #partition's shared memory weight 0 = Dedicated .. else Shared
                            if [[ -z ${LIB_PLATF_POWER_MEMENTITLEDWEIGHT:-} ]]; then
                                _temp=$(LIB_FUNC_TRIM_LEFT "${line/entitled_memory_weight=}")
                                LIB_PLATF_POWER_MEMENTITLEDWEIGHT="${_temp}"
                                readonly LIB_PLATF_POWER_MEMENTITLEDWEIGHT

                            fi
                            if [[ -z ${LIB_PLATF_POWER_MEMMODE:-} ]]; then
                                [[ ${LIB_PLATF_POWER_MEMENTITLEDWEIGHT} = 0 ]] && _temp='DEDICATED' || _temp='SHARED'
                                LIB_PLATF_POWER_MEMMODE="${_temp}"
                                readonly LIB_PLATF_POWER_MEMMODE
                            fi
            ;;

            'cmo_enabled'*)
                            if [[ -z ${LIB_PLATF_POWER_MEMAMS:-} ]]; then
                                _temp=$(LIB_FUNC_TRIM_LEFT "${line/cmo_enabled=}")
                                [[ ${_temp} = 0 ]] && _temp='DISABLED' || _temp='ENABLED'
                                LIB_PLATF_POWER_MEMAMS="${_temp}"
                                readonly LIB_PLATF_POWER_MEMAMS
                            fi
            ;;

            'shared_processor_mode'*)
                            if [[ -z ${LIB_PLATF_POWER_CPUMODE:-} ]]; then
                                _temp=$(LIB_FUNC_TRIM_LEFT "${line/shared_processor_mode=}")
                                [[ ${_temp} = 1 ]] && _temp='SHARED' || _temp='DEDICATED'
                                LIB_PLATF_POWER_CPUMODE="${_temp}"
                                readonly LIB_PLATF_POWER_CPUMODE
                            fi
            ;;

            'DedDonMode'*)
                            #DedDonMode=Dedicated Donating Mode
                            if [[ -z ${LIB_PLATF_POWER_CPUMODE_DEDDON:-} ]]; then
                                _temp=$(LIB_FUNC_TRIM_LEFT "${line/DedDonMode=}")
                                [[ ${_temp} = 1 ]] && _temp='DONATING' || _temp='DEDICATED'
                                LIB_PLATF_POWER_CPUMODE_DEDDON="${_temp}"
                                readonly LIB_PLATF_POWER_CPUMODE_DEDDON
                            fi
            ;;

            'power_mode_data'*)
                            if [[ -z ${LIB_PLATF_POWER_POWERMODE:-} ]]; then
                                #e.g. 0002000000020002
                                LIB_PLATF_POWER_POWERMODE=$(LIB_FUNC_TRIM_LEFT "${line/power_mode_data=}")
                                readonly LIB_PLATF_POWER_POWERMODE
                            fi
            ;;

            'partition_affinity_score'*)
                            if [[ -z ${LIB_PLATF_POWER_AFFINITYSCORE:-} ]]; then
                                LIB_PLATF_POWER_AFFINITYSCORE=$(LIB_FUNC_TRIM_LEFT "${line/partition_affinity_score=}")
                                readonly LIB_PLATF_POWER_AFFINITYSCORE
                            fi
            ;;

        esac

    done < "${lparcfgfile}"

    #logDebug "<${BASH_SOURCE[0]}:${FUNCNAME[0]}> # LIB_PLATF_POWER_POWERMODE : ${LIB_PLATF_POWER_POWERMODE:-}"
}

function __get_platform_power_cpu_details {

    logTrace "<${BASH_SOURCE[0]}:${FUNCNAME[*]}>"

    while read -r line ; do

        logTrace "<${FUNCNAME[0]}> # lscpu:${line}>"

        case ${line} in

        'Architecture:'*)
                            if [[ -z ${LIB_PLATF_ARCHITECTURE:-} ]]; then
                                LIB_PLATF_ARCHITECTURE=$(LIB_FUNC_TRIM_LEFT "${line/Architecture:}")
                                readonly LIB_PLATF_ARCHITECTURE
                            fi
        ;;

        'CPU(s):'*)
                            LIB_PLATF_POWER_CPU_TOTAL=$(LIB_FUNC_TRIM_LEFT "${line/CPU(s):}")
                            readonly LIB_PLATF_POWER_CPU_TOTAL
        ;;

        'Thread(s) per core:'*)
                            if [[ -z ${LIB_PLATF_CPU_THREADSPERCORE:-} ]]; then
                                LIB_PLATF_CPU_THREADSPERCORE=$(LIB_FUNC_TRIM_LEFT "${line/Thread(s) per core:}")
                                readonly LIB_PLATF_CPU_THREADSPERCORE
                            fi
        ;;

        'Core(s) per socket:'*)
                            if [[ -z ${LIB_PLATF_CPU_CORESPERSOCKET:-} ]]; then
                                LIB_PLATF_CPU_CORESPERSOCKET=$(LIB_FUNC_TRIM_LEFT "${line/Core(s) per socket:}")
                                readonly LIB_PLATF_CPU_CORESPERSOCKET
                            fi
        ;;

        'Socket(s):'*)
                            if [[ -z ${LIB_PLATF_CPU_SOCKETS:-} ]]; then
                                LIB_PLATF_CPU_SOCKETS=$(LIB_FUNC_TRIM_LEFT "${line/Socket(s):}")
                                readonly LIB_PLATF_CPU_SOCKETS
                            fi
        ;;

        'NUMA node(s):'*)
                            if [[ -z ${LIB_PLATF_CPU_NUMANODES:-} ]]; then
                                LIB_PLATF_CPU_NUMANODES=$(LIB_FUNC_TRIM_LEFT "${line/NUMA node(s):}")
                                readonly LIB_PLATF_CPU_NUMANODES
                            fi
        ;;

        'Model:'*)
                            #<Model: IBM,9119-MHE>
                            if [[ -z ${LIB_PLATF_NAME:-} ]]; then
                                LIB_PLATF_NAME=$(LIB_FUNC_TRIM_LEFT "${line/Model:}")
                                readonly LIB_PLATF_NAME
                            fi

        ;;

        'Model name:'*)
                            #Model name: POWER8 (architected), altivec supported
                            if [[ -z ${LIB_PLATF_CPU:-} ]]; then
                                LIB_PLATF_CPU=$(LIB_FUNC_TRIM_LEFT "${line/Model name:}")
                                readonly LIB_PLATF_CPU
                            fi
        ;;

        'NUMA node0'* )    break ;;

        #Virtualization information will be gathered on more reliable way

        esac

    done <<< "$(lscpu)"

    # information not provided by lscpu 'Model name'
    if [[ -z ${LIB_PLATF_CPU:-} ]]; then
        LIB_PLATF_CPU=$(grep  'cpu' -m 1 /proc/cpuinfo)
        LIB_PLATF_CPU=$(LIB_FUNC_TRIM_LEFT "${LIB_PLATF_CPU/cpu[[:space:]]*:}")
        readonly LIB_PLATF_CPU
    fi

    # have to be revised in case of any NON-IBM Power platform vendor
    if [[ -z ${LIB_PLATF_CPU_VENDOR:-} ]]; then
        LIB_PLATF_CPU_VENDOR='IBM'
        readonly LIB_PLATF_CPU_VENDOR
    fi

}

function __lib_func_get_auxvector_details {

    logTrace "<${BASH_SOURCE[0]}:${FUNCNAME[*]}>"

    while read -r line ; do

        logTrace "<${FUNCNAME[0]}> # ${line}>"

        case ${line} in

        'AT_PLATFORM:'*)
                                #AT_PLATFORM:     power7        #Processor compatibility mode
                                if [[ -z ${LIB_PLATF_POWER_PLATFORM_COMPAT:-} ]]; then
                                    LIB_PLATF_POWER_PLATFORM_COMPAT=$(LIB_FUNC_TRIM_LEFT "${line/AT_PLATFORM:}")
                                    LIB_PLATF_POWER_PLATFORM_COMPAT=${LIB_PLATF_POWER_PLATFORM_COMPAT^^}
                                    readonly LIB_PLATF_POWER_PLATFORM_COMPAT
                                fi
        ;;

        'AT_BASE_PLATFORM:'*)
                                #AT_BASE_PLATFORM:power8        #Actual processor type
                                if [[ -z ${LIB_PLATF_POWER_PLATFORM_BASE:-} ]]; then
                                    LIB_PLATF_POWER_PLATFORM_BASE=$(LIB_FUNC_TRIM_LEFT "${line/AT_BASE_PLATFORM:}")
                                    LIB_PLATF_POWER_PLATFORM_BASE=${LIB_PLATF_POWER_PLATFORM_BASE^^}
                                    readonly LIB_PLATF_POWER_PLATFORM_BASE
                                fi
        ;;

        'AT_HWCAP2:'*)
                                #AT_HWCAP2: htm-nosc vcrypto tar isel ebb dscr htm arch_2_07
                                if [[ -z ${LIB_PLATF_POWER_HWCAP2:-} ]]; then
                                    LIB_PLATF_POWER_HWCAP2=$(LIB_FUNC_TRIM_LEFT "${line/AT_HWCAP2:}")
                                    readonly LIB_PLATF_POWER_HWCAP2
                                fi
        ;;

        esac

    done <<< "$(LD_SHOW_AUXV=1 /bin/true)"

    if [[ -z ${LIB_PLATF_POWER_PLATFORM_BASE:-} ]]; then
        LIB_PLATF_POWER_PLATFORM_BASE=${LIB_PLATF_CPU%%[[:space:]]*}
        LIB_PLATF_POWER_PLATFORM_BASE=${LIB_PLATF_POWER_PLATFORM_BASE^^}
        readonly LIB_PLATF_POWER_PLATFORM_BASE
    fi

}

#============================================================
# LIB MAIN - initialization
#============================================================
function _lib_platf_power_main {

    logTrace "<${BASH_SOURCE[0]}:${FUNCNAME[*]}>"

    __lib_func_get_vendor_details
    __lib_func_get_lpar_details
    __get_platform_power_cpu_details
    __lib_func_get_auxvector_details
    __lib_func_get_hypervisor_details

    #readonly <AnyVariable>
}

#LIB LOCAL

#GLOBAL
# don't use declare here ... file is sourced within function, which would restrict visibility
# LIB_PLATF_POWER_CPU_TOTAL
# LIB_PLATF_POWER_CPUCAP
# LIB_PLATF_POWER_MEMMODE
# LIB_PLATF_POWER_MEMENTITLED
# LIB_PLATF_POWER_MEMENTITLEDMAPPED
# LIB_PLATF_POWER_MEMENTITLEDWEIGHT
# LIB_PLATF_POWER_MEMAMS
# LIB_PLATF_POWER_CPUMODE
# LIB_PLATF_POWER_CPUMODE_DEDDON
# LIB_PLATF_POWER_POWERMODE
# LIB_PLATF_POWER_PLATFORM_BASE
# LIB_PLATF_POWER_PLATFORM_COMPAT
# LIB_PLATF_POWER_HWCAP2
# LIB_PLATF_POWER_AFFINITYSCORE

#Import libraries
#shellcheck source=./saphana-logger
source "$( cd "${BASH_SOURCE[0]%/*}" && pwd )/saphana-logger" ||
                { echo 'unable to load saphana-logger library' >&2; exit 1; }
#shellcheck source=./saphana-helper-funcs
source "$( cd "${BASH_SOURCE[0]%/*}" && pwd )/saphana-helper-funcs" ||
                { echo 'unable to load saphana-helper-funcs library' >&2; exit 1; }

#CALL MAIN
_lib_platf_power_main
