#!/bin/bash
### NOTE: This file will be moved into the PACCOR project, in the "scripts" directory.
###       It's here for review, until I get permissions to the PACCOR project

### User customizable values
APP_HOME="`dirname "$0"`"
COMPONENTS_URI="" # Specify the optional components URI field
COMPONENTS_URI_LOCAL_COPY_FOR_HASH="" # If empty, the optional hashAlgorithm and hashValue fields will not be included for the URI
PROPERTIES_URI="" # Specify the optional properties URI field
PROPERTIES_URI_LOCAL_COPY_FOR_HASH="" # If empty, the optional hashAlgorithm and hashValue fields will not be included for the URI
ENTERPRISE_NUMBERS_FILE="$APP_HOME""/enterprise-numbers"
PEN_ROOT="1.3.6.1.4.1." # OID root for the private enterprise numbers
SMBIOS_SCRIPT="$APP_HOME""/smbios.sh"
HW_SCRIPT="$APP_HOME""/hw.sh" # For components not covered by SMBIOS
NVME_SCRIPT="$APP_HOME""/nvme.sh" # For nvme components, until lshw supports them

### SMBIOS Type Constants
source $SMBIOS_SCRIPT
SMBIOS_TYPE_PLATFORM="1"
SMBIOS_TYPE_CHASSIS="3"
SMBIOS_TYPE_BIOS="0"
SMBIOS_TYPE_BASEBOARD="2"
SMBIOS_TYPE_CPU="4"
SMBIOS_TYPE_RAM="17"

### hw
source $HW_SCRIPT
source $NVME_SCRIPT

### ComponentClass values
COMPCLASS_REGISTRY_TCG="2.23.133.18.3.1" # switch off values within SMBIOS to reveal accurate component classes
COMPCLASS_BASEBOARD="00030003" # these values are meant to be an example.  check the component class registry.
COMPCLASS_BIOS="00130003"
COMPCLASS_UEFI="00130002"
COMPCLASS_CHASSIS="00020001" # TODO:  chassis type is included in SMBIOS
COMPCLASS_CPU="00010002"
COMPCLASS_HDD="00070002"
COMPCLASS_NIC="00090002"
COMPCLASS_RAM="00060001"  # TODO: memory type is included in SMBIOS
COMPCLASS_GFX="00050002"

### JSON Structure Keywords
JSON_COMPONENTS="COMPONENTS"
JSON_COMPONENTSURI="COMPONENTSURI"
JSON_PROPERTIES="PROPERTIES"
JSON_PROPERTIESURI="PROPERTIESURI"
JSON_PLATFORM="PLATFORM"
#### JSON Component Keywords
JSON_COMPONENTCLASS="COMPONENTCLASS"
JSON_COMPONENTCLASSREGISTRY="COMPONENTCLASSREGISTRY"
JSON_COMPONENTCLASSVALUE="COMPONENTCLASSVALUE"
JSON_MANUFACTURER="MANUFACTURER"
JSON_MODEL="MODEL"
JSON_SERIAL="SERIAL"
JSON_REVISION="REVISION"
JSON_MANUFACTURERID="MANUFACTURERID"
JSON_FIELDREPLACEABLE="FIELDREPLACEABLE"
JSON_ADDRESSES="ADDRESSES"
JSON_ETHERNETMAC="ETHERNETMAC"
JSON_WLANMAC="WLANMAC"
JSON_BLUETOOTHMAC="BLUETOOTHMAC"
JSON_COMPONENTPLATFORMCERT="PLATFORMCERT"
JSON_ATTRIBUTECERTIDENTIFIER="ATTRIBUTECERTIDENTIFIER"
JSON_GENERICCERTIDENTIFIER="GENERICCERTIDENTIFIER"
JSON_ISSUER="ISSUER"
JSON_COMPONENTPLATFORMCERTURI="PLATFORMCERTURI"
JSON_STATUS="STATUS"
#### JSON Platform Keywords (Subject Alternative Name)
JSON_PLATFORMMODEL="PLATFORMMODEL"
JSON_PLATFORMMANUFACTURERSTR="PLATFORMMANUFACTURERSTR"
JSON_PLATFORMVERSION="PLATFORMVERSION"
JSON_PLATFORMSERIAL="PLATFORMSERIAL"
JSON_PLATFORMMANUFACTURERID="PLATFORMMANUFACTURERID"
#### JSON Platform URI Keywords
JSON_URI="UNIFORMRESOURCEIDENTIFIER"
JSON_HASHALG="HASHALGORITHM"
JSON_HASHVALUE="HASHVALUE"
#### JSON Properties Keywords
JSON_NAME="NAME"
JSON_VALUE="VALUE"
NOT_SPECIFIED="Not Specified"
CHASSIS_SERIAL_NUMBER="111111"
BASEBOARD_SERIAL_NUMBER="222222"
BIOS_SERIAL_NUMBER="333333"
PARSE_CPU_DATA_SERIAL_NUMBER="111222"
PARSE_RAM_DATA_SERIAL_NUMBER="222333"
PARSE_NIC_DATA_SERIAL_NUMBER="333444"
PARSE_HDD_DATA_SERIAL_NUMBER="444555"
PARSE_GFX_DATA_SERIAL_NUMBER="555666"

### JSON Structure Format
JSON_INTERMEDIATE_FILE_OBJECT='{
    %s
}'
JSON_PLATFORM_TEMPLATE='
    \"'"$JSON_PLATFORM"'\": {
        %s
    }'
JSON_PROPERTIESURI_TEMPLATE='
    \"'"$JSON_PROPERTIESURI"'\": {
        %s
    }'
JSON_COMPONENTSURI_TEMPLATE='
    \"'"$JSON_COMPONENTSURI"'\": {
        %s
    }'
JSON_PROPERTY_ARRAY_TEMPLATE='
    \"'"$JSON_PROPERTIES"'\": [%s
    ]'
JSON_COMPONENT_ARRAY_TEMPLATE='
    \"'"$JSON_COMPONENTS"'\": [%s
    ]'
JSON_COMPONENT_TEMPLATE='
        {
            %s
        }'
JSON_PROPERTY_TEMPLATE='
        {
            \"'"$JSON_NAME"'\": \"%s\",
            \"'"$JSON_VALUE"'\": \"%s\"
        }
'
JSON_ADDRESSES_TEMPLATE=' \"'"$JSON_ADDRESSES"'\": [%s]'
JSON_ETHERNETMAC_TEMPLATE=' {
                \"'"$JSON_ETHERNETMAC"'\": \"%s\" } '
JSON_WLANMAC_TEMPLATE=' {
                \"'"$JSON_WLANMAC"'\": \"%s\" } '
JSON_BLUETOOTHMAC_TEMPLATE=' {
                \"'"$JSON_BLUETOOTHMAC"'\": \"%s\" } '
JSON_COMPONENTCLASS_TEMPLATE=' \"'"$JSON_COMPONENTCLASS"'\": {
        \"'"$JSON_COMPONENTCLASSREGISTRY"'\": \"%s\",
        \"'"$JSON_COMPONENTCLASSVALUE"'\": \"%s\"
    }'
JSON_ATTRIBUTECERTIDENTIFIER_TEMPLATE=' \"'"$JSON_ATTRIBUTECERTIDENTIFIER"'\": {
        \"'"$JSON_HASHALG"'\": \"%s\",
        \"'"$JSON_HASHVALUE"'\": \"%s\"
    },'
JSON_GENERICCERTIDENTIFIER_TEMPLATE=' \"'"$JSON_GENERICCERTIDENTIFIER"'\": {
        \"'"$JSON_ISSUER"'\": \"%s\",
        \"'"$JSON_SERIAL"'\": \"%s\"
    },'
JSON_COMPONENTPLATFORMCERT_TEMPLATE='
    \"'"$JSON_COMPONENTPLATFORMCERT"'\": {
        %s
    }'
JSON_COMPONENTPLATFORMCERTURI_TEMPLATE='
    \"'"$JSON_COMPONENTPLATFORMCERTURI"'\": {
        %s
    }'
JSON_STATUS_TEMPLATE='
    \"'"$JSON_STATUS"'\": {

    }'

### JSON Constructor Aides
jsonComponentClass () {
    printf "$JSON_COMPONENTCLASS_TEMPLATE" "${1}" "${2}"
}
jsonManufacturer () {
    manufacturer=$(printf '\"'"$JSON_MANUFACTURER"'\": \"%s\"' "${1}")
    #tmpManufacturerId=$(queryForPen "${1}")
    #if [ -n "$tmpManufacturerId" ] && [ "$tmpManufacturerId" != "$PEN_ROOT" ]; then
    #    tmpManufacturerId=$(jsonManufacturerId "$tmpManufacturerId")
    #    manufacturer="$manufacturer"",""$tmpManufacturerId"
    #fi
    printf "$manufacturer"
}
jsonModel () {
    printf '\"'"$JSON_MODEL"'\": \"%s\"' "${1}"
}
jsonSerial () {
    printf '\"'"$JSON_SERIAL"'\": \"%s\"' "${1}"
}
jsonRevision () {
    printf '\"'"$JSON_REVISION"'\": \"%s\"' "${1}"
}
jsonManufacturerId () {
    printf '\"'"$JSON_MANUFACTURERID"'\": \"%s\"' "${1}"
}
jsonFieldReplaceable () {
    printf '\"'"$JSON_FIELDREPLACEABLE"'\": \"%s\"' "${1}"
}
jsonEthernetMac () {
    printf "$JSON_ETHERNETMAC_TEMPLATE" "${1}"
}
jsonWlanMac () {
    printf "$JSON_WLANMAC_TEMPLATE" "${1}"
}
jsonBluetoothMac () {
    printf "$JSON_BLUETOOTHMAC_TEMPLATE" "${1}"
}
jsonPlatformModel () {
    printf '\"'"$JSON_PLATFORMMODEL"'\": \"%s\"' "${1}"
}
jsonPlatformManufacturerStr () {
    manufacturer=$(printf '\"'"$JSON_PLATFORMMANUFACTURERSTR"'\": \"%s\"' "${1}")
    #tmpManufacturerId=$(queryForPen "${1}")
    #if [ -n "$tmpManufacturerId" ] && [ "$tmpManufacturerId" != "$PEN_ROOT" ]; then
    #    tmpManufacturerId=$(jsonPlatformManufacturerId "$tmpManufacturerId")
    #    manufacturer="$manufacturer"",""$tmpManufacturerId"
    #fi
    printf "$manufacturer"
}
jsonPlatformVersion () {
    printf '\"'"$JSON_PLATFORMVERSION"'\": \"%s\"' "${1}"
}
jsonPlatformSerial () {
    printf '\"'"$JSON_PLATFORMSERIAL"'\": \"%s\"' "${1}"
}
jsonPlatformManufacturerId () {
    printf '\"'"$JSON_PLATFORMMANUFACTURERID"'\": \"%s\"' "${1}"
}
queryForPen () {
    pen=$(grep -B 1 "^[ \t]*""${1}""$" "$ENTERPRISE_NUMBERS_FILE" | sed -n '1p' | tr -d [:space:])
    printf "%s%s" "$PEN_ROOT" "$pen"
}
jsonProperty () {
    if [ -n "${1}" ] && [ -n "${2}" ]; then
        if [ -n "${3}" ]; then
            printf "$JSON_PROPERTY_TEMPLATE" "${1}" "${2}" "${3}"
        else
            printf "$JSON_PROPERTY_TEMPLATE" "${1}" "${2}"
        fi
    fi
}
jsonUri () {
    printf '\"'"$JSON_URI"'\": \"%s\"' "${1}"
}
jsonHashAlg () {
    printf '\"'"$JSON_HASHALG"'\": \"%s\"' "${1}"
}
jsonHashValue () {
    printf '\"'"$JSON_HASHVALUE"'\": \"%s\"' "${1}"
}
toCSV () {
    old="$IFS"
    IFS=','
    value="$*"
    value=$(printf "$value" | tr -s , | sed -e '1s/^[,]*//' | sed -e '$s/[,]*$//')
    printf "$value"
}
jsonAddress () {
    printf "$JSON_ADDRESSES_TEMPLATE" "$(toCSV "$@")"
}
jsonComponent () {
    printf "$JSON_COMPONENT_TEMPLATE" "$(toCSV "$@")"
}
jsonComponentArray () {
    printf "$JSON_COMPONENT_ARRAY_TEMPLATE" "$(toCSV "$@")"
}
jsonPropertyArray () {
    if [ "$#" -ne 0 ]; then
        printf "$JSON_PROPERTY_ARRAY_TEMPLATE" "$(toCSV "$@")"
    fi
}
jsonPlatformObject () {
    printf "$JSON_PLATFORM_TEMPLATE" "$(toCSV "$@")"
}
jsonComponentsUri () {
    if [ -n "$COMPONENTS_URI" ]; then
        componentsUri=$(jsonUri "$COMPONENTS_URI")
        componentsUriDetails=""
        if [ -n "$PROPERTIES_URI_LOCAL_COPY_FOR_HASH" ]; then
            hashAlg="2.16.840.1.101.3.4.2.1" # SHA256, see https://tools.ietf.org/html/rfc5754 for other common hash algorithm IDs
            hashValue=$(sha256sum "$COMPONENTS_URI_LOCAL_COPY_FOR_HASH" | sed -r 's/^([0-9a-f]+).*/\1/' | tr -d [:space:] | xxd -r -p | base64 -w 0)
            hashAlgStr=$(jsonHashAlg "$hashAlg")
            hashValueStr=$(jsonHashValue "$hashValue")
            propertiesUriDetails="$hashAlgStr"",""$hashValueStr"
        fi
    printf "$JSON_COMPONENTSURI_TEMPLATE" "$(toCSV "$componentsUri" "$componentsUriDetails")"
    fi
}
jsonPropertiesUri () {
    if [ -n "$PROPERTIES_URI" ]; then
        propertiesUri=$(jsonUri "$PROPERTIES_URI")
        propertiesUriDetails=""
        if [ -n "$PROPERTIES_URI_LOCAL_COPY_FOR_HASH" ]; then
            hashAlg="2.16.840.1.101.3.4.2.1" # SHA256, see https://tools.ietf.org/html/rfc5754 for other common hash algorithm IDs
            hashValue=$(sha256sum "$PROPERTIES_URI_LOCAL_COPY_FOR_HASH" | sed -r 's/^([0-9a-f]+).*/\1/' | tr -d [:space:] | xxd -r -p | base64 -w 0)
            hashAlgStr=$(jsonHashAlg "$hashAlg")
            hashValueStr=$(jsonHashValue "$hashValue")
            propertiesUriDetails="$hashAlgStr"",""$hashValueStr"
        fi| sed 's/^[ \t]*//;s/[ \t]*$//'
    printf "$JSON_PROPERTIESURI_TEMPLATE" "$(toCSV "$propertiesUri" "$propertiesUriDetails")"
    fi
}
jsonIntermediateFile () {
    printf "$JSON_INTERMEDIATE_FILE_OBJECT" "$(toCSV "$@")"
}
standardizeMACAddr () {
    mac=$(printf "${1}" | tr -d "[[:space:]]:-" | awk '{ print toupper($0) }')
    printf "$mac"
}



## Some of the commands below require root.
if [ "$EUID" -ne 0 ]
  then echo "Please run as root"
  exit
fi

### Gather platform details for the subject alternative name
dmidecodeParseTypeAssumeOneHandle "$SMBIOS_TYPE_PLATFORM"
platformManufacturer=$(dmidecodeGetString $(dmidecodeGetByte "0x4"))
platformModel=$(dmidecodeGetString $(dmidecodeGetByte "0x5"))
platformVersion=$(dmidecodeGetString $(dmidecodeGetByte "0x6"))
platformSerial=$(dmidecodeGetString $(dmidecodeGetByte "0x7"))

if [[ -z "${platformManufacturer// }" ]]; then
    platformManufacturer="$NOT_SPECIFIED"
fi
platformManufacturer=$(echo "$platformManufacturer" | sed 's/^[ \t]*//;s/[ \t]*$//')
platformManufacturer=$(jsonPlatformManufacturerStr "$platformManufacturer")

if [[ -z "${platformModel// }" ]]; then
    platformModel="$NOT_SPECIFIED"
fi
platformModel=$(echo "$platformModel" | sed 's/^[ \t]*//;s/[ \t]*$//')
platformModel=$(jsonPlatformModel "$platformModel")

if [[ -z "${platformVersion// }" ]]; then
    platformVersion="$NOT_SPECIFIED"
fi
platformVersion=$(echo "$platformVersion" | sed 's/^[ \t]*//;s/[ \t]*$//')
platformVersion=$(jsonPlatformVersion "$platformVersion")

if ! [[ -z "${platformSerial// }" ]]; then
    platformSerial=$(echo "$platformSerial" | sed 's/^[ \t]*//;s/[ \t]*$//')
    platformSerial=$(jsonPlatformSerial "$platformSerial")
fi
platform=$(jsonPlatformObject "$platformManufacturer" "$platformModel" "$platformVersion" "$platformSerial")



### Gather component details
dmidecodeParseTypeAssumeOneHandle "$SMBIOS_TYPE_CHASSIS"
chassisClass=$(jsonComponentClass "$COMPCLASS_REGISTRY_TCG" "$COMPCLASS_CHASSIS")
chassisManufacturer=$(dmidecodeGetString $(dmidecodeGetByte "0x4"))
chassisModel=$(dmidecodeGetByte "0x5")
chassisModel=$(printf "%d" "0x""$chassisModel") # Convert to decimal
chassisSerial=$(dmidecodeGetString $(dmidecodeGetByte "0x7"))
chassisRevision=$(dmidecodeGetString $(dmidecodeGetByte "0x6"))

if [[ -z "${chassisManufacturer// }" ]]; then
    chassisManufacturer="$NOT_SPECIFIED"
fi
chassisManufacturer=$(echo "$chassisManufacturer" | sed 's/^[ \t]*//;s/[ \t]*$//')
chassisManufacturer=$(jsonManufacturer "$chassisManufacturer")

if [[ -z "${chassisModel// }" ]]; then
    chassisModel="$NOT_SPECIFIED"
fi
chassisModel=$(echo "$chassisModel" | sed 's/^[ \t]*//;s/[ \t]*$//')
chassisModel=$(jsonModel "$chassisModel")

chassisOptional=""
if ! [[ -z "${chassisSerial// }" ]]; then
    chassisSerial=$(echo "$chassisSerial" | sed 's/^[ \t]*//;s/[ \t]*$//')
    chassisSerial=$(jsonSerial "$chassisSerial")
    chassisOptional="$chassisOptional"",""$chassisSerial"
fi
if ! [[ -z "${chassisRevision// }" ]]; then
    chassisRevision=$(echo "$chassisRevision" | sed 's/^[ \t]*//;s/[ \t]*$//')
    chassisRevision=$(jsonRevision "$chassisRevision")
    chassisOptional="$chassisOptional"",""$chassisRevision"
fi
chassisOptional=$(printf "$chassisOptional" | cut -c2-)
# Use default SN#
if [[ -z "${chassisOptional// }" ]]; then
	chassisSerial=$(jsonSerial "$CHASSIS_SERIAL_NUMBER")
    chassisOptional="$chassisOptional"",""$chassisSerial"
fi

componentChassis=$(jsonComponent "$chassisClass" "$chassisManufacturer" "$chassisModel" "$chassisOptional")

### Gather baseboard details
dmidecodeParseTypeAssumeOneHandle "$SMBIOS_TYPE_BASEBOARD"
baseboardClass=$(jsonComponentClass "$COMPCLASS_REGISTRY_TCG" "$COMPCLASS_BASEBOARD")
baseboardManufacturer=$(dmidecodeGetString $(dmidecodeGetByte "0x4"))
baseboardModel=$(dmidecodeGetString $(dmidecodeGetByte "0x5"))
baseboardSerial=$(dmidecodeGetString $(dmidecodeGetByte "0x7"))
baseboardRevision=$(dmidecodeGetString $(dmidecodeGetByte "0x6"))
baseboardFeatureFlags=$(dmidecodeGetByte "0x9")
baseboardFeatureFlags=$(printf "%d" "0x""$baseboardFeatureFlags") # Convert to decimal
baseboardReplaceableIndicator="28"
baseboardFieldReplaceableAnswer="false"
if (((baseboardFeatureFlags&baseboardReplaceableIndicator)!=0)); then
    baseboardFieldReplaceableAnswer="true"
fi
baseboardFieldReplaceable=$(jsonFieldReplaceable "$baseboardFieldReplaceableAnswer")

if [[ -z "${baseboardManufacturer// }" ]]; then
    baseboardManufacturer="$NOT_SPECIFIED"
fi
baseboardManufacturer=$(echo "$baseboardManufacturer" | sed 's/^[ \t]*//;s/[ \t]*$//')
baseboardManufacturer=$(jsonManufacturer "$baseboardManufacturer")

if [[ -z "${baseboardModel// }" ]]; then
    baseboardModel="$NOT_SPECIFIED"
fi
baseboardModel=$(echo "$baseboardModel" | sed 's/^[ \t]*//;s/[ \t]*$//')
baseboardModel=$(jsonModel "$baseboardModel")

baseboardOptional=""
if ! [[ -z "${baseboardSerial// }" ]]; then
    baseboardSerial=$(echo "$baseboardSerial" | sed 's/^[ \t]*//;s/[ \t]*$//')
    baseboardSerial=$(jsonSerial "$baseboardSerial")
    baseboardOptional="$baseboardOptional"",""$baseboardSerial"
fi
if ! [[ -z "${baseboardRevision// }" ]]; then
    baseboardRevision=$(echo "$baseboardRevision" | sed 's/^[ \t]*//;s/[ \t]*$//')
    baseboardRevision=$(jsonRevision "$baseboardRevision")
    baseboardOptional="$baseboardOptional"",""$baseboardRevision"
fi
baseboardOptional=$(printf "$baseboardOptional" | cut -c2-)
# Use default SN#
if [[ -z "${baseboardOptional// }" ]]; then
	baseboardSerial=$(jsonSerial "$BASEBOARD_SERIAL_NUMBER")
    baseboardOptional="$baseboardOptional"",""$baseboardSerial"
fi

componentBaseboard=$(jsonComponent "$baseboardClass" "$baseboardManufacturer" "$baseboardModel" "$baseboardFieldReplaceable" "$baseboardOptional")

### Gather BIOS details
dmidecodeParseTypeAssumeOneHandle "$SMBIOS_TYPE_BIOS"
biosClass=$(jsonComponentClass "$COMPCLASS_REGISTRY_TCG" "$COMPCLASS_BIOS")
biosManufacturer=$(dmidecodeGetString $(dmidecodeGetByte "0x4"))
biosModel=""
biosSerial=""
biosRevision=$(dmidecodeGetString $(dmidecodeGetByte "0x5"))

if [[ -z "${biosManufacturer// }" ]]; then
    biosManufacturer="$NOT_SPECIFIED"
fi
biosManufacturer=$(echo "$biosManufacturer" | sed 's/^[ \t]*//;s/[ \t]*$//')
biosManufacturer=$(jsonManufacturer "$biosManufacturer")

if [[ -z "${biosModel// }" ]]; then
    biosModel="$NOT_SPECIFIED"
fi
biosModel=$(echo "$biosModel" | sed 's/^[ \t]*//;s/[ \t]*$//')
biosModel=$(jsonModel "$biosModel")

biosOptional=""
if ! [[ -z "${biosSerial// }" ]]; then
    biosSerial=$(echo "$biosSerial" | sed 's/^[ \t]*//;s/[ \t]*$//')
    biosSerial=$(jsonSerial "$biosSerial")
    biosOptional="$biosOptional"",""$biosSerial"
fi
if ! [[ -z "${biosRevision// }" ]]; then
    biosRevision=$(echo "$biosRevision" | sed 's/^[ \t]*//;s/[ \t]*$//')
    biosRevision=$(jsonRevision "$biosRevision")
    biosOptional="$biosOptional"",""$biosRevision"
fi
biosOptional=$(printf "$biosOptional" | cut -c2-)
# Use default SN#
if [[ -z "${biosOptional// }" ]]; then
	biosSerial=$(jsonSerial "$BIOS_SERIAL_NUMBER")
    biosOptional="$biosOptional"",""$biosSerial"
fi

componentBios=$(jsonComponent "$biosClass" "$biosManufacturer" "$biosModel" "$biosOptional")

parseCpuData () {

    dmidecodeHandles "$SMBIOS_TYPE_CPU"

    notReplaceableIndicator="6"
    tmpData=""
    numHandles=$(dmidecodeNumHandles)
    class=$(jsonComponentClass "$COMPCLASS_REGISTRY_TCG" "$COMPCLASS_CPU")

    for ((i = 0 ; i < numHandles ; i++ )); do
        dmidecodeParseHandle "${tableHandles[$i]}"

    manufacturer=$(dmidecodeGetString $(dmidecodeGetByte "0x7"))
    model=$(dmidecodeGetByte "0x6")
        model=$(printf "%d" "0x""$model") # Convert to decimal
    serial=$(dmidecodeGetString $(dmidecodeGetByte "0x20"))
    revision=$(dmidecodeGetString $(dmidecodeGetByte "0x10"))
        processorUpgrade=$(dmidecodeGetByte "0x19")
        processorUpgrade=$(printf "%d" "0x""$processorUpgrade") # Convert to decimal

    if [[ -z "${manufacturer// }" ]]; then
        manufacturer="$NOT_SPECIFIED"
    fi
    manufacturer=$(echo "$manufacturer" | sed 's/^[ \t]*//;s/[ \t]*$//')
    manufacturer=$(jsonManufacturer "$manufacturer")

    if [[ -z "${model// }" ]]; then
        model="$NOT_SPECIFIED"
    fi
    model=$(echo "$model" | sed 's/^[ \t]*//;s/[ \t]*$//')
    model=$(jsonModel "$model")

    optional=""
    if ! [[ -z "${serial// }" ]]; then
        serial=$(echo "$serial" | sed 's/^[ \t]*//;s/[ \t]*$//')
        serial=$(jsonSerial "$serial")
        optional="$optional"",""$serial"
    fi
    if ! [[ -z "${revision// }" ]]; then
        revision=$(echo "$revision" | sed 's/^[ \t]*//;s/[ \t]*$//')
        revision=$(jsonRevision "$revision")
        optional="$optional"",""$revision"
    fi
    optional=$(printf "$optional" | cut -c2-)
	# Use default SN#
	if [[ -z "${optional// }" ]]; then
		serial=$(jsonSerial "$PARSE_CPU_DATA_SERIAL_NUMBER")
	    optional="$optional"",""$serial"
	    PARSE_CPU_DATA_SERIAL_NUMBER=$((PARSE_CPU_DATA_SERIAL_NUMBER + 1))
	fi

        replaceable="true"
        if [ $processorUpgrade -eq $notReplaceableIndicator ]; then
            replaceable="false"
        fi
        replaceable=$(jsonFieldReplaceable "$replaceable")

        newCpuData=$(jsonComponent "$class" "$manufacturer" "$model" "$replaceable" "$optional")
        tmpData="$tmpData"",""$newCpuData"
    done

    # remove leading comma
    tmpData=$(printf "$tmpData" | cut -c2-)

    printf "$tmpData"
}

parseRamData () {
    dmidecodeHandles "$SMBIOS_TYPE_RAM"

    replaceable=$(jsonFieldReplaceable "true")
    tmpData=""
    numHandles=$(dmidecodeNumHandles)
    class=$(jsonComponentClass "$COMPCLASS_REGISTRY_TCG" "$COMPCLASS_RAM")

    for ((i = 0 ; i < numHandles ; i++ )); do
        dmidecodeParseHandle "${tableHandles[$i]}"

    manufacturer=$(dmidecodeGetString $(dmidecodeGetByte "0x17"))
    model=$(dmidecodeGetString $(dmidecodeGetByte "0x1A"))
    serial=$(dmidecodeGetString $(dmidecodeGetByte "0x18"))
    revision=$(dmidecodeGetString $(dmidecodeGetByte "0x19"))

        if ([[ -z "${manufacturer// }" ]] && [[ -z "${model// }" ]] && [[ -z "${serial// }" ]] && [[ -z "${revision// }" ]]); then
            continue
        fi

    if [[ -z "${manufacturer// }" ]]; then
        manufacturer="$NOT_SPECIFIED"
    fi
    manufacturer=$(echo "$manufacturer" | sed 's/^[ \t]*//;s/[ \t]*$//')
    manufacturer=$(jsonManufacturer "$manufacturer")

    if [[ -z "${model// }" ]]; then
        model="$NOT_SPECIFIED"
    fi
    model=$(echo "$model" | sed 's/^[ \t]*//;s/[ \t]*$//')
    model=$(jsonModel "$model")

    optional=""
    if ! [[ -z "${serial// }" ]]; then
        serial=$(echo "$serial" | sed 's/^[ \t]*//;s/[ \t]*$//')
        serial=$(jsonSerial "$serial")
        optional="$optional"",""$serial"
    fi
    if ! [[ -z "${revision// }" ]]; then
        revision=$(echo "$revision" | sed 's/^[ \t]*//;s/[ \t]*$//')
        revision=$(jsonRevision "$revision")
        optional="$optional"",""$revision"
    fi
    optional=$(printf "$optional" | cut -c2-)
	# Use default SN#
	if [[ -z "${optional// }" ]]; then
		serial=$(jsonSerial "$PARSE_RAM_DATA_SERIAL_NUMBER")
	    optional="$optional"",""$serial"
	    PARSE_RAM_DATA_SERIAL_NUMBER=$((PARSE_RAM_DATA_SERIAL_NUMBER + 1))
	fi

        newRamData=$(jsonComponent "$class" "$manufacturer" "$model" "$replaceable" "$optional")
        tmpData="$tmpData"",""$newRamData"
    done

    # remove leading comma
    tmpData=$(printf "$tmpData" | cut -c2-)

    printf "$tmpData"
}

# Write script to parse multiple responses
# Network:
# lshw description: type of address.
#                 : Ethernet interface, Wireless interface, Bluetooth wireless interface
#           vendor: manufacturer
#          product: model
#           serial: address & serial number
#          version: revision
#
# Example:
# ADDRESS1=$(jsonEthernetMac "AB:CD:EE:EE:DE:34")
# ADDR_LIST=$(jsonAddress "$ADDRESS1" "$ADDRESS2")
parseNicData () {
    lshwNetwork

    replaceable=$(jsonFieldReplaceable "true")
    tmpData=""
    numHandles=$(lshwNumBusItems)
    class=$(jsonComponentClass "$COMPCLASS_REGISTRY_TCG" "$COMPCLASS_NIC")

    for ((i = 0 ; i < numHandles ; i++ )); do
        manufacturer=$(lshwGetVendorIDFromBusItem "$i")
    	model=$(lshwGetProductIDFromBusItem "$i")
    	serialConstant=$(lshwGetLogicalNameFromBusItem "$i")
        serialConstant=$(ethtoolPermAddr "$serialConstant")
        serialConstant=$(standardizeMACAddr "${serialConstant}")
        serial=""
    	revision=$(lshwGetVersionFromBusItem "$i")

        if [[ -z "${manufacturer// }" ]] && [[ -z "${model// }" ]] && (! [[ -z "${serialConstant// }" ]] || ! [[ -z "${revision// }" ]]); then
            manufacturer=$(lshwGetVendorNameFromBusItem "$i")
        model=$(lshwGetProductNameFromBusItem "$i")
        fi

    if [[ -z "${manufacturer// }" ]]; then
        manufacturer="$NOT_SPECIFIED"
    fi
    manufacturer=$(echo "$manufacturer" | sed 's/^[ \t]*//;s/[ \t]*$//')
    manufacturer=$(jsonManufacturer "$manufacturer")

    if [[ -z "${model// }" ]]; then
        model="$NOT_SPECIFIED"
    fi
    model=$(echo "$model" | sed 's/^[ \t]*//;s/[ \t]*$//')
    model=$(jsonModel "$model")

    optional=""
    if ! [[ -z "${serialConstant// }" ]]; then
        serial=$(echo "$serialConstant" | sed 's/^[ \t]*//;s/[ \t]*$//')
        serial=$(jsonSerial "$serialConstant")
        optional="$optional"",""$serial"
    fi
    if ! [[ -z "${revision// }" ]]; then
        revision=$(echo "$revision" | sed 's/^[ \t]*//;s/[ \t]*$//' | awk '{ print toupper($0) }')
        revision=$(jsonRevision "$revision")
        optional="$optional"",""$revision"
    fi
        bluetoothCap=$(lshwBusItemBluetoothCap "$i")
        ethernetCap=$(lshwBusItemEthernetCap "$i")
        wirelessCap=$(lshwBusItemWirelessCap "$i")

        if ([ -n "$bluetoothCap" ] || [ -n "$ethernetCap" ] || [ -n "$wirelessCap" ]) && ! [[ -z "${serialConstant// }" ]]; then
            thisAddress=
            if [ -n "$wirelessCap" ]; then
                thisAddress=$(jsonWlanMac "$serialConstant")
            elif [ -n "$bluetoothCap" ]; then
                thisAddress=$(jsonBluetoothMac "$serialConstant")
            elif [ -n "$ethernetCap" ]; then
                thisAddress=$(jsonEthernetMac "$serialConstant")
            fi
            if [ -n "$thisAddress" ]; then
                thisAddress=$(jsonAddress "$thisAddress")
                optional="$optional"",""$thisAddress"
            fi
        fi
    optional=$(printf "$optional" | cut -c2-)
	# Use default SN#
	if [[ -z "${optional// }" ]]; then
		serial=$(jsonSerial "$PARSE_NIC_DATA_SERIAL_NUMBER")
	    optional="$optional"",""$serial"
	    PARSE_NIC_DATA_SERIAL_NUMBER=$((PARSE_NIC_DATA_SERIAL_NUMBER + 1))
	fi

        newNicData=$(jsonComponent "$class" "$manufacturer" "$model" "$replaceable" "$optional")
        tmpData="$tmpData"",""$newNicData"
    done

    # remove leading comma
    tmpData=$(printf "$tmpData" | cut -c2-)

    printf "$tmpData"
}

parseHddData () {
    lshwDisk

    replaceable=$(jsonFieldReplaceable "true")
    tmpData=""
    numHandles=$(lshwNumBusItems)
    class=$(jsonComponentClass "$COMPCLASS_REGISTRY_TCG" "$COMPCLASS_HDD")

    for ((i = 0 ; i < numHandles ; i++ )); do
        manufacturer=$(lshwGetVendorIDFromBusItem "$i")
	    model=$(lshwGetProductIDFromBusItem "$i")
	    serial=$(lshwGetSerialFromBusItem "$i")
	    revision=$(lshwGetVersionFromBusItem "$i")

        if [[ -z "${manufacturer// }" ]] && [[ -z "${model// }" ]] && (! [[ -z "${serial// }" ]] || ! [[ -z "${revision// }" ]]); then
            model=$(lshwGetProductNameFromBusItem "$i")
            manufacturer=""
            revision="" # Seeing inconsistent behavior cross-OS for this case, will return
        fi

    if [[ -z "${manufacturer// }" ]]; then
        manufacturer="$NOT_SPECIFIED"
    fi
    manufacturer=$(echo "$manufacturer" | sed 's/^[ \t]*//;s/[ \t]*$//')
    manufacturer=$(jsonManufacturer "$manufacturer")

    if [[ -z "${model// }" ]]; then
        model="$NOT_SPECIFIED"
    fi
    model=$(echo "$model" | sed 's/^[ \t]*//;s/[ \t]*$//')
    model=$(jsonModel "$model")

    optional=""
    if ! [[ -z "${serial// }" ]]; then
        serial=$(echo "$serial" | sed 's/^[ \t]*//;s/[ \t]*$//')
        serial=$(jsonSerial "$serial")
        optional="$optional"",""$serial"
    fi
    if ! [[ -z "${revision// }" ]]; then
        revision=$(echo "$revision" | sed 's/^[ \t]*//;s/[ \t]*$//' | awk '{ print toupper($0) }')
        revision=$(jsonRevision "$revision")
        optional="$optional"",""$revision"
    fi
    optional=$(printf "$optional" | cut -c2-)
	# Use default SN#
	if [[ -z "${optional// }" ]]; then
		serial=$(jsonSerial "PARSE_HDD_DATA_SERIAL_NUMBER")
	    optional="$optional"",""$serial"
	    PARSE_HDD_DATA_SERIAL_NUMBER=$((PARSE_HDD_DATA_SERIAL_NUMBER + 1))
	fi

        newHddData=$(jsonComponent "$class" "$manufacturer" "$model" "$replaceable" "$optional")
        tmpData="$tmpData"",""$newHddData"
    done

    # remove leading comma
    tmpData=$(printf "$tmpData" | cut -c2-)

    printf "$tmpData"
}

parseNvmeData () {
    nvmeParse

    replaceable=$(jsonFieldReplaceable "true")
    tmpData=""
    numHandles=$(nvmeNumDevices)
    class=$(jsonComponentClass "$COMPCLASS_REGISTRY_TCG" "$COMPCLASS_HDD")

    for ((i = 0 ; i < numHandles ; i++ )); do
        manufacturer="" # Making this appear as it does on windows, lshw doesn't see nvme drives and nvme-cli doesn't return a manufacturer field
        model=$(nvmeGetModelNumberForDevice "$i")
        serial=$(nvmeGetNguidForDevice "$i")

        if [[ $serial =~ ^[0]+$ ]]; then
           serial=$(nvmeGetEuiForDevice "$i")
        fi
        revision="" # empty for a similar reason to the manufacturer field

    if [[ -z "${manufacturer// }" ]]; then
        manufacturer="$NOT_SPECIFIED"
    fi
    manufacturer=$(echo "$manufacturer" | sed 's/^[ \t]*//;s/[ \t]*$//')
    manufacturer=$(jsonManufacturer "$manufacturer")

    if [[ -z "${model// }" ]]; then
        model="$NOT_SPECIFIED"
    fi
    model=$(echo "${model:0:16}" | sed 's/^[ \t]*//;s/[ \t]*$//') # limited to 16 characters for compatibility to windows, then trimmed
    model=$(jsonModel "$model")

    optional=""
    if ! [[ -z "${serial// }" ]]; then
        serial=$(echo "${serial^^}" | sed 's/^[ \t]*//;s/[ \t]*$//' | sed 's/.\{4\}/&_/g' | sed 's/_$/\./')
        serial=$(jsonSerial "$serial")
        optional="$optional"",""$serial"
    fi
    optional=$(printf "$optional" | cut -c2-)

        newHddData=$(jsonComponent "$class" "$manufacturer" "$model" "$replaceable" "$optional")
        tmpData="$tmpData"",""$newHddData"
    done

    # remove leading comma
    tmpData=$(printf "$tmpData" | cut -c2-)

    printf "$tmpData"
}

parseGfxData () {
    lshwDisplay

    replaceable=$(jsonFieldReplaceable "true")
    tmpData=""
    numHandles=$(lshwNumBusItems)
    class=$(jsonComponentClass "$COMPCLASS_REGISTRY_TCG" "$COMPCLASS_GFX")

    for ((i = 0 ; i < numHandles ; i++ )); do
        manufacturer=$(lshwGetVendorIDFromBusItem "$i")
    model=$(lshwGetProductIDFromBusItem "$i")
    serial=$(lshwGetSerialFromBusItem "$i")
    revision=$(lshwGetVersionFromBusItem "$i")

        if [[ -z "${manufacturer// }" ]] && [[ -z "${model// }" ]] && (! [[ -z "${serial// }" ]] || ! [[ -z "${revision// }" ]]); then
            manufacturer=$(lshwGetVendorNameFromBusItem "$i")
        	model=$(lshwGetProductNameFromBusItem "$i")
        fi

    if [[ -z "${manufacturer// }" ]]; then
        manufacturer="$NOT_SPECIFIED"
    fi
    manufacturer=$(echo "$manufacturer" | sed 's/^[ \t]*//;s/[ \t]*$//')
    manufacturer=$(jsonManufacturer "$manufacturer")

    if [[ -z "${model// }" ]]; then
        model="$NOT_SPECIFIED"
    fi
    model=$(echo "$model" | sed 's/^[ \t]*//;s/[ \t]*$//')
    model=$(jsonModel "$model")

    optional=""
    if ! [[ -z "${serial// }" ]]; then
        serial=$(echo "$serial" | sed 's/^[ \t]*//;s/[ \t]*$//')
        serial=$(jsonSerial "$serial")
        optional="$optional"",""$serial"
    fi
    if ! [[ -z "${revision// }" ]]; then
        revision=$(echo "$revision" | sed 's/^[ \t]*//;s/[ \t]*$//' | awk '{ print toupper($0) }')
        revision=$(jsonRevision "$revision")
        optional="$optional"",""$revision"
    fi
    optional=$(printf "$optional" | cut -c2-)
	# Use default SN#
	if [[ -z "${optional// }" ]]; then
		serial=$(jsonSerial "PARSE_GFX_DATA_SERIAL_NUMBER")
	    optional="$optional"",""$serial"
	    PARSE_GFX_DATA_SERIAL_NUMBER=$((PARSE_GFX_DATA_SERIAL_NUMBER + 1))
	fi

        newGfxData=$(jsonComponent "$class" "$manufacturer" "$model" "$replaceable" "$optional")
        tmpData="$tmpData"",""$newGfxData"
    done

    # remove leading comma
    tmpData=$(printf "$tmpData" | cut -c2-)

    printf "$tmpData"
}


### Gather property details
property1=$(jsonProperty "uname -r" "$(uname -r)")  ## Example1
property2=$(jsonProperty "OS Release" "$(grep 'PRETTY_NAME=' /etc/os-release | sed 's/[^=]*=//' | sed -e 's/^[[:space:]\"]*//' | sed -e 's/[[:space:]\"]*$//')") ## Example2

### Collate the component details
componentsCPU=$(parseCpuData)
componentsRAM=$(parseRamData)
componentsNIC=$(parseNicData)
componentsHDD=$(parseHddData)
componentsGFX=$(parseGfxData)
componentArray=$(jsonComponentArray "$componentChassis" "$componentBaseboard" "$componentBios" "$componentsCPU" "$componentsRAM" "$componentsNIC" "$componentsHDD" "$componentsNVMe" "$componentsGFX")

### Collate the property details
propertyArray=$(jsonPropertyArray "$property1" "$property2")

### Construct the final JSON object
FINAL_JSON_OBJECT=$(jsonIntermediateFile "$platform" "$componentArray" "$propertyArray")

### Collate the URI details, if parameters above are blank, the fields will be excluded from the final JSON structure
if [ -n "$COMPONENTS_URI" ]; then
    componentsUri=$(jsonComponentsUri)
    FINAL_JSON_OBJECT="$FINAL_JSON_OBJECT"",""$componentsUri"
fi
if [ -n "$PROPERTIES_URI" ]; then
    propertiesUri=$(jsonPropertiesUri)
    FINAL_JSON_OBJECT="$FINAL_JSON_OBJECT"",""$propertiesUri"
fi

printf "$FINAL_JSON_OBJECT""\n\n"