#!/bin/bash

### COMMON VARS
scriptDir=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
outFile="$scriptDir/inventory.json"
jq_bin="/opt/colibri/jq/jq"

if [ ! -f "$jq_bin" ]; then
    >&2 echo "Embedded in Colibri Agent JQ tool is not detected, try to detect local installation"
    jq_bin=$(which jq 2>/dev/null)
    if [ -z "$jq_bin" ]; then
        >&2 echo "Local system JQ not detected as well. Exit script"
        exit 1
    fi
fi

offset=$(date '+%z' | sed 's/./&:/3')
timestamp=$(date '+%Y-%m-%dT%H:%M:%S.%7N')$offset
meshPath=/usr/local/mesh_services/meshagent/meshagent
[ -x $meshPath ] && meshAgentId=$($meshPath -nodeid)

### VARS for DeviceInventory
domain=$(hostname -d)
hostname=$(hostname -s)
fqdn=$(hostname -f)
if [ "${fqdn,,}" == "${hostname,,}" ]; then
    fqdn=$(host -Tta "$(hostname -s)" | grep "has address" 2>/dev/null | awk '{print $1}') | head -n 1
    if [ -z "$fqdn" ]; then
        fqdn=$hostname
    fi
fi
if [ "${domain,,}" == "${hostname,,}" ]; then
    # Domain is not detected
    if [ "${fqdn,,}" != "${hostname,,}" ]; then
        domain=$(echo "$fqdn" | cut -d. -f2-)
    else
        domain=""
    fi
fi

cpuCount=$(/usr/sbin/dmidecode -t4 -q | grep "Processor Information" 2>/dev/null | wc -l)
model=$(/usr/sbin/dmidecode --string system-product-name)
di_manufacturer=$(/usr/sbin/dmidecode --string system-manufacturer)
lastLogonUser=$(last -w 2>/dev/null | grep -v reboot 2>/dev/null | head -n 1 | awk '{print $1}')
physicalMemory=$(free -m | awk 'NR==2 {print $2}')
memSlots=$(dmidecode -t 17 -q | grep -c "Memory Device")
memModules=$(dmidecode -t 17 -q | tr -d '\t' | grep "^Size:"| grep -vc "No Module Installed")
driveNames=$(lsblk -nr | awk '{if($6=="disk") print $1}')
bootDrv=$(lsblk -no pkname "$(findmnt -n / | awk '{gsub(/\[.*\]/,"",$2)};{ print $2 }')")

if [[ $(last --time-format iso >/dev/null 2>&1) ]]; then
    # modern 'last' used
    # take the latest entry except reboot and shutdown
    lastLogonTime=$(last --time-format iso 2>/dev/null | grep -Ev 'reboot|shutdown' 2>/dev/null | head -n 1 | awk '{split($(NF-3),date,"+"); print date[1]".0000000+"date[2]}')
    # take the latest entry within reboot and shutdown
    lastRebootTime=$(last --time-format iso 2>/dev/null | grep -E 'reboot|shutdown' 2>/dev/null | head -n 1 | awk '{split($5,date,"+"); print date[1]".0000000+"date[2]}')
else
    # old 'last' used
    # take the latest entry except reboot and shutdown
    lastLogonTime=$(date '+%Y-%m-%dT%H:%M:%S.%7N%:z' -d "$(last -F 2>/dev/null | grep -Ev 'reboot|shutdown' 2>/dev/null | head -n 1 | awk '{ print (NF < 12)? $(NF-5) " " $(NF-6) " " $(NF-3) " " $(NF-4) : $(NF-9) " " $(NF-10) " " $(NF-7) " " $(NF-8) }')" 2>/dev/null)
    # take the latest entry within reboot and shutdown
    lastRebootTime=$(date '+%Y-%m-%dT%H:%M:%S.%7N%:z' -d "$(last -F 2>/dev/null | grep -E 'reboot|shutdown' 2>/dev/null | head -n 1 | awk '{ print (NF < 12)? $(NF-4) " " $(NF-5) " " $(NF-6) " " $(NF-3) : $(NF-9) " " $(NF-10) " " $(NF-7) " " $(NF-8) }')" 2>/dev/null)
fi
[ -z "$lastLogonTime" ] || ! date -d "$lastLogonTime" >/dev/null 2>&1 && lastLogonTime=$(date '+%Y-%m-%dT%H:%M:%S.%7N%:z')
[ -z "$lastRebootTime" ] || ! date -d "$lastRebootTime" >/dev/null 2>&1 && lastRebootTime=$(date -d "$(uptime -s)" '+%Y-%m-%dT%H:%M:%S.%7N%:z')

defBrowseRaw=$(xdg-settings get default-web-browser 2>/dev/null)
httpMimePath="/usr/share/applications/mimeapps.list"
if [[ -z $defBrowseRaw ]]; then [ -f $httpMimePath ] && defBrowseRaw=$(cat $httpMimePath | grep 'handler/https' | awk -F'=' '{print $2}'); fi
if [[ "$defBrowseRaw" == "chromium-gost.desktop" ]]; then defBrowser="Chromium"
elif [[ "$defBrowseRaw" == "firefox.desktop" ]]; then defBrowser="Firefox"
else defBrowser=$defBrowseRaw
fi

powerPath="/sys/devices/system/cpu/cpu0/cpufreq/scaling_governor"
[[ -f $powerPath ]] && powerPlan=$(cat $powerPath)

dnsSrvs="$(cat /etc/resolv.conf | grep ^nameserver | awk 'NR > 1 { printf(", ")} { printf("%s", $2) }')"
firstDNS="$(echo "$dnsSrvs" | awk -F ', ' '{print $1}')"
if [ -n "$firstDNS" ] && ! grep -q "^127" <<< "$firstDNS"; then 
    mainIP=$(ip -o route get "$firstDNS" | grep -Po '(?<=src )(\d{1,3}.){4}' | tr -d " ")
else
    defGW=$(ip route | grep ^default | awk '{print $3}')
    mainIP=$(ip -o route get $defGW | grep -Po '(?<=src )(\d{1,3}.){4}' | tr -d " ")
fi

defBootldr=$(echo $((`cat /proc/sys/kernel/bootloader_type`>>4)))
case $defBootldr in
    0) bootloader="LILO" ;;
    1) bootloader="Loadlin" ;;
    2) bootloader="bootsect-loader" ;;
    3) bootloader="Syslinux" ;;
    4) bootloader="Etherboot/gPXE/iPXE" ;;
    5) bootloader="ELILO" ;;
    7) bootloader="GRUB" ;;
    8) bootloader="U-Boot" ;;
    9) bootloader="Xen" ;;
    A) bootloader="Gujin" ;;
    B) bootloader="Qemu" ;;
    C) bootloader="Arcturus Networks uCbootloader" ;;
    D) bootloader="kexec-tools" ;;
    E) bootloader="Extended" ;;
    F) bootloader="Special" ;;
    10) bootloader="Reserved" ;;
    11) bootloader="Minimal Linux Bootloader" ;;
    12) bootloader="OVMF UEFI virtualization stack" ;;
    *) bootloader="Unknown"
esac

desktopEnvPath="/usr/share/xsessions"
[[ -d $desktopEnvPath ]] && desktopEnv=$(ls $desktopEnvPath | awk -F'.' 'NR > 1 {printf(", ")} { printf("%s", $1) }')

[[ $(LVM_SUPPRESS_FD_WARNINGS=1 lvdisplay 2>/dev/null) ]] && lvmStatus="In use" ||  lvmStatus="Not in use"

## FIREWALL
if [[ $(ufw status 2>/dev/null) ]]; then
    fwStatus=$(ufw status 2>/dev/null | grep Status | awk -F':' '{print $2}')
    fwName="ufw"
fi
if [[ $(systemctl status firewalld 2>/dev/null) ]]; then
    fwStatus=$(systemctl status firewalld 2>/dev/null | grep Active: | awk '{print $2}')
    fwName="firewalld"
fi
## DBG

## DEVICE TYPE
deviceType=$(cat /sys/class/dmi/id/chassis_type)
if [[ "8 9 10 14" =~ (\b$deviceType\b) ]]; then psSysType="Laptop"
elif [[ "3 4 6 7" =~ (\b$deviceType\b) ]]; then psSysType="Desktop"
elif [[ $(systemd-detect-virt) != "none" ]]; then psSysType="Virtual Machine"
else psSysType=$(/usr/sbin/dmidecode --string chassis-type)  ## this operation sooo long 
fi

DetectPlatform() {
    ### DEFINE PLATFORMS
    deb=("Debian" "AstraLinux" "Astra" "Ubuntu" "OSNova")
    rpm=("RedHat" "AlterOS" "RED SOFT" "MSVSphere" "CentOS" "rosa")
    alt=("ALT" "Simply Linux" "starter kit" "ALT Workstation" "altlinux" "ALT SP Workstation" "ALT SP Workstation 11100-01")
    
    # Detect current OS
    currOs=$(lsb_release -is 2>/dev/null || cat /etc/os-release | grep '^ID=' | awk -F= 'gsub(/^"|"$|/,"",$2) {print $2}')

    for rpm_plat in "${rpm[@],,}";do
        if [ "$rpm_plat" == "${currOs,,}" ]; then 
            echo "rpm"
            break
        fi
    done

    for deb_plat in "${deb[@],,}";do
        if [ "$deb_plat" == "${currOs,,}" ]; then 
            echo "deb"
            break
        fi
    done

    for alt_plat in "${alt[@],,}";do
        if [ "$alt_plat" == "${currOs,,}" ]; then 
            echo "alt"
            break
        fi
    done
}

GlobalArchitecture() {
    appArch=$1
    if [[ $appArch == "noarch" || $appArch == "all" ]]; then globArch="3"
    elif [[ $appArch == "x86_64" || $appArch == "amd64" ]]; then globArch="1"
    elif [[ $appArch == "i586" || $appArch == "i386" || $appArch == "i686" ]]; then globArch="2"
    fi
    echo "$globArch"
}

AddToRootPath() {
    rootPath=$1
    subPath=$2
    [ "$rootPath" != "[]" ] && echo "${rootPath::-1},\"$subPath\"]" || echo "[\"$subPath\"]"
}

AppInventory() {
    inventory_file=$1
    parent_root=$2
    subTree="AppInventory"
    # shellcheck disable=SC2016
    "$jq_bin" --argjson root "$parent_root" --arg subTree "$subTree" 'getpath($root) +=  { ($subTree) : [] }' "$inventory_file" > "${inventory_file%json}tmp" && mv -f "${inventory_file%json}tmp" "$inventory_file"
    root=""&&root=$(AddToRootPath "$parent_root" "AppInventory")
    case "$(DetectPlatform)" in
        deb)
            dpkgApps_raw=$(dpkg-query -W --showformat '${Package};${Version};${Architecture};${Maintainer};${Section}\n')
            IFS=$'\n' read -d '' -r -a dpkgApps_arr <<< "$dpkgApps_raw"
            for appLine in "${dpkgApps_arr[@]}"; do
                package=$(awk -F\; '{ print $1 }' <<< "$appLine")
                appInstDate=$(awk -v OFS=T -v package="$package" '$4=="installed" {sub(/:.*/, "", $5); if ($5==package) {print $1, $2}}' /var/log/dpkg.log* | tail -1)
                [ -n "$appInstDate" ] && appInstDate="$(date -d "$appInstDate" --iso-8601=ns | sed -E 's/(.*),([0-9]{7}).*(\+|-.*)/\1.\2\3/')"
                appArch=$(awk -F\; '{ print $3 }' <<< "$appLine")
                globArch=$(GlobalArchitecture "$appArch")
                app_entry="${appLine};${globArch};${appInstDate}"
                # shellcheck disable=SC2016
                "$jq_bin" --arg appEntry "$app_entry" --argjson root "$root" 'getpath($root) += [ $appEntry | split(";") | {Name:(.[0]), Version:.[1], Architecture:.[2], GlobalArchitecture:(.[5]|tonumber? // 0),Maintainer:.[3], Section:.[4], InstallDate:.[6], Vendor: ""} |del(..|nulls) | with_entries(select(.value != "" or .key == "Vendor")) ]' "$inventory_file" > "${inventory_file%json}tmp" && mv -f "${inventory_file%json}tmp" "$inventory_file"
                jq_rc=$?
                [ $jq_rc -ne 0 ] && exit 2
            done
        ;;

        rpm | alt)
            rpmApps_raw=$(rpm -qa --queryformat '%{name};%{version}-%{release};%{arch};%{packager};%{group};%{vendor};%{installtime};%{archivesize};%{sigmd5}\n'| \
            awk -F\; -v OFS=";" -v offset="$offset"  '{ DT =  strftime("%FT%T.0000000"offset, $7); {print $1,$2,$3,$4,$5,$6,DT,$8,$9}}')
            IFS=$'\n' read -d '' -r -a rpmApps_arr <<< "$rpmApps_raw"
            for appLine in "${rpmApps_arr[@]}"; do
                appArch=$(awk -F\; '{ print $3 }' <<< "$appLine")
                globArch=$(GlobalArchitecture "$appArch")
                app_entry="${appLine};${globArch}"
                # shellcheck disable=SC2016
                "$jq_bin" --arg appEntry "$app_entry" --argjson root "$root" 'getpath($root) += [ $appEntry | split(";") | {Name:.[0], Version:.[1], Architecture:.[2], GlobalArchitecture:(.[9]|tonumber? // 0), Maintainer:.[3], Section:.[4], InstallDate:.[6], Vendor:.[5], Size:(.[7]|tonumber? // 0), MD5Hash:.[8] } |del(..|nulls) | with_entries(select(.value != ""))]' "$inventory_file" > "${inventory_file%json}tmp" && mv -f "${inventory_file%json}tmp" "$inventory_file"
                jq_rc=$?
                [ $jq_rc -ne 0 ] && exit 2
            done
        ;;
    esac
}

ServiceStateInventory() {
    inventory_file=$1
    parent_root=$2
    subTree="ServiceStateInventory"
    # shellcheck disable=SC2016
    "$jq_bin" --argjson root "$parent_root" --arg subTree "$subTree" 'getpath($root) +=  { ($subTree) : [] }' "$inventory_file" > "${inventory_file%json}tmp" && mv -f "${inventory_file%json}tmp" "$inventory_file"
    root=""&&root=$(AddToRootPath "$parent_root" "ServiceStateInventory")

    services_raw=$(systemctl list-units --type=service --full --plain --no-legend)
    svc_list=$(echo "$services_raw"| awk -v OFS=';' '{if ($2=="loaded") {print $1, "true", $3, $4";"} else {print $1, "false", $3, $4";"}}')
    IFS=$'\n' read -d '' -r -a svc_arr <<< "$svc_list"
    svc_count="${#svc_arr[@]}"
    dsc_list=$(echo "$services_raw" | awk '{$1=$2=$3=$4=""}1' | sed 's/^[[:space:]]*//g')
    IFS=$'\n' read -d '' -r -a dsc_arr <<< "$dsc_list"
    for (( i=1; i < $svc_count; i++ )); do
        svc_entry="${svc_arr[$i]}${dsc_arr[$i]}"
        # shellcheck disable=SC2016
        "$jq_bin" --arg svcEntry "$svc_entry" --argjson root "$root" 'getpath($root) += [ $svcEntry | split(";") | {Name:.[0], Status:.[2], Loaded:.[1]|test("true"), Substatus:.[3], Description:.[4]} ]'  "$inventory_file" > "${inventory_file%json}tmp" && mv -f "${inventory_file%json}tmp" "$inventory_file"
    done
}

## DEVICE INVENTORY
OSInfo() {
    inventory_file=$1
    parent_root=$2
    subTree="OSInfo"
    # shellcheck disable=SC2016
    "$jq_bin" --argjson root "$parent_root" --arg subTree "$subTree" 'getpath($root) +=  { ($subTree) : {}}' "$inventory_file" > "${inventory_file%json}tmp" && mv -f "${inventory_file%json}tmp" "$inventory_file"
    root=""&&root=$(AddToRootPath "$parent_root" "OSInfo")

    osName=$(awk -F= '$1=="NAME" {print $2}' /etc/os-release | tr -d \")
    bootTimeS=$(systemd-analyze | cut -d ' ' -f7 | head -n 1 | tr -d ' ' | tr -d 's' | awk -F min '{ if (NF>1) print int($1*60+$2+0.5) ; else print int($1+0.5)}') || bootTimeS=0
    bootTimeU=$(systemd-analyze | cut -d ' ' -f4 | head -n 1 | tr -d ' ' | tr -d 's' | awk -F min '{ if (NF>1) print int($1*60+$2+0.5) ; else print int($1+0.5)}') || bootTimeU=0
    bootTimeF=$(systemd-analyze | cut -d '=' -f2 | head -n 1 | tr -d ' ' | tr -d 's' | awk -F min '{ if (NF>1) print int($1*60+$2+0.5) ; else print int($1+0.5)}') || bootTimeF=0

    
    case $(DetectPlatform) in
        deb)
            type=2
            rebootCounter=$(last -s -7days | grep -c reboot) || rebootCounter=0 ## maybee replace "0" with null for detection problems in json file ?
            crashCounter=$(last -s -7days | grep -c crash) || crashCounter=0
            ;;
        rpm)
            type=3
            last -s -7days >/dev/null 2>&1
            is_new_version=$?
            if [ $is_new_version -eq 0 ]; then
                rebootCounter=$(last -s -7days | grep -c reboot) || rebootCounter=0
                crashCounter=$(last -s -7days | grep -c crash) || crashCounter=0
            else
                rebootCounter=$(last reboot | grep ^reboot | awk '{print $5,$6,$7,$8}' | while read line ; do [[ ! $(date --date="$line" -Iseconds) < $(date --date='-7days' -Iseconds) ]] && echo $line ; done | wc -l) || rebootCounter=0
                crashCounter=$(last crash | grep ^crash | awk '{print $5,$6,$7,$8}' | while read line ; do [[ ! $(date --date="$line" -Iseconds) < $(date --date='-7days' -Iseconds) ]] && echo $line ; done | wc -l) || crashCounter=0
            fi
            ;;
        alt)
            type=4
            rebootCounter=$(last reboot | grep ^reboot | awk '{print $5,$6,$7,$8}' | while read line ; do [[ ! $(date --date="$line" -Iseconds) < $(date --date='-7days' -Iseconds) ]] && echo $line ; done | wc -l) || rebootCounter=0
            crashCounter=$(last crash | grep ^crash | awk '{print $5,$6,$7,$8}' | while read line ; do [[ ! $(date --date="$line" -Iseconds) < $(date --date='-7days' -Iseconds) ]] && echo $line ; done | wc -l) || crashCounter=0
            ;;
    esac

    kernelRelease=$(uname -r)
    for arch in "x86_64" "amd64" "aarch64" ; do [ "$(uname -m)" == "$arch" ] && architecture=1 || architecture=2; done;

    case $(DetectPlatform) in
        deb)
            case $osName in
                "Astra Linux")
                    osVer=$(cat /etc/astra_version)
                    case $(cut -c1 /etc/astra_version) in
                        1) astraEdition="Special Edition" ;;
                        2) astraEdition="Common Edition" ;;
                    esac ;;
                *)
                    osVer=$(awk -F= '$1=="VERSION_ID" {print $2}' /etc/os-release | tr -d \") ;;
            esac ;;
        rpm)
            case $osName in
                "CentOS Linux")
                    osVer=$(cat /etc/centos-release) ;;
                *)
                    osVer=$(awk -F= '$1=="VERSION_ID" {print $2}' /etc/os-release | tr -d \") ;;
            esac ;;
        alt)
            osVer=$(cat /etc/altlinux-release) ;;
    esac

    language=""
    kbLayouts=""
    systemctl --quiet is-active systemd-localed.service
    localed_status=$?
    if [ $localed_status -eq 0 ]; then
        language=$(localectl | grep "System Locale" | awk '{print $3}' | tr -d "LANG=")
        kbLayouts=$(localectl | grep "X11 Layout" | awk '{print $3}')
    fi
    instDate=$(ls -lact -l --time-style='+%Y-%m-%dT%H:%M:%S.%7N%:z' /etc | awk -v OFS="" 'END {print $6}')

    os_info="$osName;$bootTimeS;$bootTimeU;$bootTimeF;$rebootCounter;$crashCounter;$type;$kernelRelease;$architecture;$osVer;$astraEdition;$language;$kbLayouts;$instDate;"
    # shellcheck disable=SC2016
    "$jq_bin" --arg osInfo "$os_info" --argjson root "$root" 'getpath($root) += ($osInfo | split(";") | { Name:.[0], BootTimeS:(.[1]|tonumber? // 0), BootTimeU:(.[2]|tonumber? // 0), 
                BootTimeF:(.[3]|tonumber? // 0), RebootCounter:(.[4]|tonumber? // 0), CrashCounter:(.[5]|tonumber? // 0), Type:(.[6]|tonumber? // 0), 
                KernelRelease:.[7], Architecture:.[8]|tonumber, Version:.[9], AstraEdition:.[10], Language:.[11], 
                KeyboardLayouts:.[12], InstallDate:.[13]} |del(..|nulls) | with_entries(select(.value != "")) )'  "$inventory_file" > "${inventory_file%json}tmp" && mv -f "${inventory_file%json}tmp" "$inventory_file"
}

BIOSInfo() {
    inventory_file=$1
    parent_root=$2
    subTree="BIOSInfo"
    # shellcheck disable=SC2016
    "$jq_bin" --argjson root "$parent_root" --arg subTree "$subTree" 'getpath($root) +=  { ($subTree) : {}}' "$inventory_file" > "${inventory_file%json}tmp" && mv -f "${inventory_file%json}tmp" "$inventory_file"
    root=""&&root=$(AddToRootPath "$parent_root" "BIOSInfo")

    sysSerNum=$(/usr/sbin/dmidecode --string system-serial-number)
    sysUUID=$(/usr/sbin/dmidecode --string system-uuid)
    biosVer=$(/usr/sbin/dmidecode --string bios-version)
    biosRelDate=$(date --date="$(/usr/sbin/dmidecode --string bios-release-date)" '+%Y-%m-%dT%H:%M:%S.%7N')$offset
    [ -d /sys/firmware/efi ] && frmwrType="UEFI" || frmwrType="BIOS"

    bios_info="$sysSerNum;$sysUUID;$biosVer;$biosRelDate;$frmwrType"
    # shellcheck disable=SC2016
    "$jq_bin" --arg biosInfo "$bios_info" --argjson root "$root" 'getpath($root) += ($biosInfo | split(";") | {SerialNumber:.[0], UUID:.[1], Version:.[2], Date:.[3], FirmwareType:.[4]} |del(..|nulls) | with_entries(select(.value != "")) )' "$inventory_file" > "${inventory_file%json}tmp" && mv -f "${inventory_file%json}tmp" "$inventory_file"
}

GPUInfo() {
    inventory_file=$1
    parent_root=$2
    subTree="GPUInfo"
    # shellcheck disable=SC2016
    "$jq_bin" --argjson root "$parent_root" --arg subTree "$subTree" 'getpath($root) +=  { ($subTree) : []}' "$inventory_file" > "${inventory_file%json}tmp" && mv -f "${inventory_file%json}tmp" "$inventory_file"
    root=""&&root=$(AddToRootPath "$parent_root" "GPUInfo")

    gpuIdlist="$(lspci | grep VGA | awk '{print $1}')"
    for i in ${!gpuIdlist[*]}; do
        if [[ ! ${gpuIdlist[$i]} == "" ]]; then
            gpuName="GPU"$(($i+1))
            gpuModel=$(lspci -v -s "${gpuIdlist[$i]}" | awk -F ': ' 'NR==1{{print $2}}')
            gpuSubSys=$(lspci -v -s "${gpuIdlist[$i]}" | grep 'Subsystem:' | awk -F ': ' '{print $2}')
            gruDrVer=$(lspci -v -s "${gpuIdlist[$i]}" | grep 'Kernel driver' | awk -F ': ' '{print $2}')
            gpuSize=$(lspci -v -s "${gpuIdlist[$i]}" | grep ' prefetchable' | awk -F 'size=' '{gsub(/]/,""); print $2}')
            gpu_entry="$gpuName;$gpuModel;$gpuSubSys;$gruDrVer;$gpuSize"
            # shellcheck disable=SC2016
            "$jq_bin" --arg gpuEntry "$gpu_entry" --argjson root "$root" 'getpath($root) += [ $gpuEntry | split(";") | {GPUName:.[0], GPUModel:.[1], GPUSubsystem:.[2], GPUDriverVersion:.[3], GPUSize:.[4]} ]'  "$inventory_file" > "${inventory_file%json}tmp" && mv -f "${inventory_file%json}tmp" "$inventory_file"
        fi
    done
}

DisplayInfo() { # This attribute requires hwinfo package installation
    inventory_file=$1
    parent_root=$2
    subTree="DisplayInfo"
    # shellcheck disable=SC2016
    "$jq_bin" --argjson root "$parent_root" --arg subTree "$subTree" 'getpath($root) +=  { ($subTree) : []}' "$inventory_file" > "${inventory_file%json}tmp" && mv -f "${inventory_file%json}tmp" "$inventory_file"
    root=""&&root=$(AddToRootPath "$parent_root" "DisplayInfo")

    # Test if hwinfo installed
    if [ -n "$(which hwinfo 2>/dev/null)" ]; then
        # Test if hwinfo detected any displays
        dpl_list_raw=$(hwinfo --monitor)
        if [ -n "$dpl_list_raw" ]; then
            dpl_info_raw=$(echo -e "$dpl_list_raw\n"  | awk 'BEGIN { FS=":"; OFS=";" } /Device|Model|Serial ID|Vendor/ { gsub(/^[ \t]+/,"",$1); gsub(/^[" \t]+|[" \t]+$/,"",$2); gsub(/"/,"",$2);line = (line ? line OFS : "") $1 "=" $2 } /^$/ {print line; line=""}')
            IFS=$'\n' read -d '' -r -a dpl_info_arr <<< "$dpl_info_raw"
            for dpl_info in "${dpl_info_arr[@]}"
            do
                dpl_info_json=$("$jq_bin" -n '{}')
                name=$(echo "$dpl_info" | sed -n -E  's/.*Device=([^;]*).*/\1/p')
                # shellcheck disable=SC2016
                [ "$name" != "$dpl_info" ] && dpl_info_json=$("$jq_bin" -n --arg name "$name" --argjson base "$dpl_info_json" '$base |. += { Name: $name }')
                model=$(echo "$dpl_info" | sed -n -E  's/.*Model=([^;]*).*/\1/p')
                # shellcheck disable=SC2016
                [ -n "$model" ] && dpl_info_json=$("$jq_bin" -n --arg model "$model" --argjson base "$dpl_info_json" '$base |. += { ModelName: $model }')
                sn=$(echo "$dpl_info" | sed -n -E  's/.*Serial ID=([^;]*).*/\1/p')
                # shellcheck disable=SC2016
                #TODO Add to model
                [ -n "$sn" ] && dpl_info_json=$("$jq_bin" -n --arg sn "$sn" --argjson base "$dpl_info_json" '$base |. += { SerialNumber: $sn }')
                vendor=$(echo "$dpl_info" | sed -n -E  's/.*Vendor=([^;]*).*/\1/p')
                # shellcheck disable=SC2016
                [ -n "$vendor" ] && dpl_info_json=$("$jq_bin" -n --arg vendor "$vendor" --argjson base "$dpl_info_json" '$base |. += { VendorName: $vendor }')
                # shellcheck disable=SC2016
                "$jq_bin" --argjson dplInfo "$dpl_info_json"  --argjson root "$root" 'getpath($root) += [$dplInfo]' "$inventory_file" > "${inventory_file%json}tmp" && mv -f "${inventory_file%json}tmp" "$inventory_file"
            done
        fi
    fi
}

CPUInfo () {
    inventory_file=$1
    parent_root=$2
    subTree="CPUInfo"
    # shellcheck disable=SC2016
    "$jq_bin" --argjson root "$parent_root" --arg subTree "$subTree" 'getpath($root) +=  { ($subTree) : []}' "$inventory_file" > "${inventory_file%json}tmp" && mv -f "${inventory_file%json}tmp" "$inventory_file"
    root=""&&root=$(AddToRootPath "$parent_root" "CPUInfo")

    cpu_info_raw=$(/usr/sbin/dmidecode -t4 -q | \
    awk 'BEGIN { FS=":"; OFS=";" } /Socket Designation|Type|Family|Manufacturer|Version|Current Speed|Core Count|Thread Count/ && !/Signature/ { gsub(/^[ \t]+/,"",$1); gsub(/^[ \t]+/,"",$2); if ($1 == "Current Speed") gsub(" ..z$", "",$2); line = (line ? line OFS : "") $1 "=" $2 } /^$/ {print line; line=""}'| \
    awk -v arch="$(uname -m)" '{FS="\t"; OFS=";"; print $0, arch}')
    IFS=$'\n' read -d '' -r -a cpu_info_arr <<< "$cpu_info_raw"
    for cpu_info in "${cpu_info_arr[@]}"
    do
        cpu_info_json=$("$jq_bin" -n '{}')
        socket=$(echo "$cpu_info" | sed -n -E  's/.*Socket Designation=([^;]*).*/\1/p')
        # shellcheck disable=SC2016
        [ "$socket" != "$cpu_info" ] && cpu_info_json=$("$jq_bin" -n --arg socket "$socket" --argjson base "$cpu_info_json" '$base |. += { Socket: $socket }')
        type=$(echo "$cpu_info" | sed -n -E  's/.*Type=([^;]*).*/\1/p')
        # shellcheck disable=SC2016
        [ -n "$type" ] && cpu_info_json=$("$jq_bin" -n --arg type "$type" --argjson base "$cpu_info_json" '$base |. += { Type: $type }')
        family=$(echo "$cpu_info" | sed -n -E  's/.*Family=([^;]*).*/\1/p')
        # shellcheck disable=SC2016
        [ -n "$family" ] && cpu_info_json=$("$jq_bin" -n --arg family "$family" --argjson base "$cpu_info_json" '$base |. += { Family: $family }')
        manufacturer=$(echo "$cpu_info" | sed -n -E  's/.*Manufacturer=([^;]*).*/\1/p')
        # shellcheck disable=SC2016
        [ -n "$manufacturer" ] && cpu_info_json=$("$jq_bin" -n --arg manufacturer "$manufacturer" --argjson base "$cpu_info_json" '$base |. += { Manufacturer: $manufacturer }')
        name=$(echo "$cpu_info" | sed -n -E  's/.*Version=([^;]*).*/\1/p')
        # shellcheck disable=SC2016
        [ -n "$name" ] && cpu_info_json=$("$jq_bin" -n --arg name "$name" --argjson base "$cpu_info_json" '$base |. += { Name: $name }')
        frequency=$(echo "$cpu_info" | sed -n -E  's/.*Current Speed=([^;]*).*/\1/p')
        # shellcheck disable=SC2016
        [ -n "$frequency" ] && cpu_info_json=$("$jq_bin" -n --arg frequency "$frequency" --argjson base "$cpu_info_json" '$base |. += { Frequency: ($frequency|tonumber? // 0) }')
        cores=$(echo "$cpu_info" | sed -n -E  's/.*Core Count=([^;]*).*/\1/p')
        # shellcheck disable=SC2016
        [ -n "$cores" ] && cpu_info_json=$("$jq_bin" -n --arg cores "$cores" --argjson base "$cpu_info_json" '$base |. += { Cores: ($cores|tonumber? // 0) }')
        logical=$(echo "$cpu_info" | sed -n -E  's/.*Thread Count=([^;]*).*/\1/p')
        # shellcheck disable=SC2016
        [ -n "$logical" ] && cpu_info_json=$("$jq_bin" -n --arg logical "$logical" --argjson base "$cpu_info_json" '$base |. += { Logical: ($logical|tonumber? // 0) }')
        architecture=$(echo "$cpu_info" | sed -n -E  's/.*Type=([^;]*).*/\1/p')
        # shellcheck disable=SC2016
        [ -n "$architecture" ] && cpu_info_json=$("$jq_bin" -n --arg architecture "$architecture" --argjson base "$cpu_info_json" '$base |. += { Architecture: $architecture }')

        # shellcheck disable=SC2016
        "$jq_bin" --argjson cpuInfo "$cpu_info_json"  --argjson root "$root" 'getpath($root) += [$cpuInfo]' "$inventory_file" > "${inventory_file%json}tmp" && mv -f "${inventory_file%json}tmp" "$inventory_file"
    done
}

PCIDevices() {
    inventory_file=$1
    parent_root=$2
    subTree="PCIDevices"
    # shellcheck disable=SC2016
    "$jq_bin" --argjson root "$parent_root" --arg subTree "$subTree" 'getpath($root) +=  { ($subTree) : []}' "$inventory_file" > "${inventory_file%json}tmp" && mv -f "${inventory_file%json}tmp" "$inventory_file"
    root=""&&root=$(AddToRootPath "$parent_root" "PCIDevices")

    IFS=$'\n' read -d '' -r -a pci_arr <<< "$(lspci)"
    for pci in "${pci_arr[@]}"; do
        # shellcheck disable=SC2016
        "$jq_bin" --arg pciEntry "$pci" --argjson root "$root" 'getpath($root) += [  {Name:$pciEntry} ]'  "$inventory_file" > "${inventory_file%json}tmp" && mv -f "${inventory_file%json}tmp" "$inventory_file"
    done
}

ProxySettings() {
    inventory_file=$1
    parent_root=$2
    subTree="ProxySettings"
    # shellcheck disable=SC2016
    "$jq_bin" --argjson root "$parent_root" --arg subTree "$subTree" 'getpath($root) +=  { ($subTree) : []}' "$inventory_file" > "${inventory_file%json}tmp" && mv -f "${inventory_file%json}tmp" "$inventory_file"
    root=""&&root=$(AddToRootPath "$parent_root" "ProxySettings")

    for var in "http_proxy" "HTTP_PROXY" "https_proxy" "HTTPS_PROXY" "ftp_proxy" "FTP_PROXY" "socks_proxy" "SOCKS_PROXY" "no_proxy" "NO_PROXY" ; do
        envvar=$(env | grep $var)
        if [ -n "$envvar" ]; then
            # shellcheck disable=SC2016
            "$jq_bin" --arg envvar "$envvar" --argjson root "$root" 'getpath($root) += [ $envvar | split("=") | {Name:.[0], Value:.[1]} ]'  "$inventory_file" > "${inventory_file%json}tmp" && mv -f "${inventory_file%json}tmp" "$inventory_file"
        fi
    done
}

AutoStartApplications() {
    inventory_file=$1
    parent_root=$2
    subTree="AutoStartApplications"
    # shellcheck disable=SC2016
    "$jq_bin" --argjson root "$parent_root" --arg subTree "$subTree" 'getpath($root) +=  { ($subTree) : []}' "$inventory_file" > "${inventory_file%json}tmp" && mv -f "${inventory_file%json}tmp" "$inventory_file"
    root=""&&root=$(AddToRootPath "$parent_root" "AutoStartApplications")

    autostart_apps=""
    [[ -d /etc/init.d/ ]] && autostart_apps+="$(ls -1 /etc/init.d/)\n"
    [[ -d /etc/xdg/autostart/ ]] && autostart_apps+="$(ls -1 /etc/xdg/autostart/ | sed -e 's/\.desktop$//')"
    IFS=$'\n' read -d '' -r -a autostart_apps_arr <<< "$(echo -e "$autostart_apps" | sort -ud)"
    for aa in "${autostart_apps_arr[@]}"; do
        # shellcheck disable=SC2016
        "$jq_bin" --arg aa "$aa" --argjson root "$root" 'getpath($root) += [ {Name:$aa} ]'  "$inventory_file" > "${inventory_file%json}tmp" && mv -f "${inventory_file%json}tmp" "$inventory_file"
    done
}

NetworkAdapters() {
    inventory_file=$1
    parent_root=$2
    mainIP=$3
    subTree="NetworkAdapters"
    "$jq_bin" --argjson root "$parent_root" --arg subTree "$subTree" 'getpath($root) +=  { ($subTree) : []}' "$inventory_file" > "${inventory_file%json}tmp" && mv -f "${inventory_file%json}tmp" "$inventory_file"
    root=""&&root=$(AddToRootPath "$parent_root" "NetworkAdapters")

    macs=()
    for i in $(basename -a /sys/class/net/* | grep -v 'lo'); do
        iInfo=""
        ifaceType=""
        IPv4=""
        subnet=""
        IPv6=""
        prefix=""
        mac=""
        speed=""
        duplex=""
        state=""
        gateway=""
        ipType=""
        [[ $(systemctl is-active NetworkManager) == "active" ]] && iInfo=$(nmcli con show | grep {item} | awk -F'  ' '{{print $1}}')
        ##### DETECT INTERFACE TYPE
        iType=$(cat /sys/class/net/$i/type)
        if [[ -f /sys/class/net/$i/type ]]; then
            if [[ $iType == "1" ]]; then
                ifaceType="eth"
                if [ -f /sys/class/net/$i/wireless ] || [ -f /sys/class/net/$i/phy80211 ]; then ifaceType="wireless"
                elif [ -f /sys/class/net/$i/bridge ]; then ifaceType="bridge"
                elif [ -f /proc/net/vlan/$i ]; then ifaceType="vlan"
                elif [ -f /sys/class/net/$i/bonding ]; then ifaceType="bond"
                elif [ -f /sys/class/net/$i/tun_flags ]; then ifaceType="tun"
                elif [ -f /sys/class/net/$i/tap_flags ]; then ifaceType="tap"
                fi
            elif [[ $iType == "24" ]]; then ifaceType="eth"
            elif [[ $iType == "32" ]]; then
                if [ -f /sys/class/net/$i/bonding ]; then ifaceType="bond"
                elif [ -f /sys/class/net/$i/create_child ]; then ifaceType="infini_band"
                else ifaceType="infini_band_child"
                fi
            elif [[ $iType == "512" ]]; then ifaceType="ppp"
            elif [[ $iType == "768" ]]; then ifaceType="ip_ip_tunnel"
            elif [[ $iType == "769" ]]; then ifaceType="ip6_ip6_tunnel"
            elif [[ $iType == "772" ]]; then ifaceType="lo"
            elif [[ $iType == "776" ]]; then ifaceType="ipv6_in_ipv4"
            elif [[ $iType == "778" ]]; then ifaceType="gre_over_ip"
            elif [[ $iType == "783" ]]; then ifaceType="irda"
            elif [[ $iType == "801" ]]; then ifaceType="wlan_aux"
            elif [[ $iType == "65534" ]]; then ifaceType="tun"
            else ifaceType="unknown"
            fi
        fi
        #####
        if_IPv4=$(ip -f inet addr show $i)
        if [[ $? -eq 0 ]] && [[ ! -z $if_IPv4 ]]; then
            IPv4=$(ip -f inet addr show $i | grep 'inet' | awk '{{print $2}}' | awk -F'/' '{{print $1}}')
            subnet=$(ip -f inet addr show $i | grep 'inet' | awk '{{print $2}}' | awk -F'/' '{{print $2}}')
        fi

        if_IPv6=$(ip -f inet6 addr show $i)
        if [[ $? -eq 0 ]] && [[ ! -z $if_IPv6 ]]; then
            IPv6=$(ip -f inet6 addr show $i | grep 'inet6' | awk '{{print $2}}' | awk -F'/' '{{print $1}}')
            prefix=$(ip -f inet6 addr show $i  | grep 'inet' | awk '{{print $2}}' | awk -F'/' '{{print $2}}')
        fi

        mac=$(awk '{gsub(/:/,"-"); print $0}' /sys/class/net/$i/address)
        speed=$(cat /sys/class/net/$i/speed 2>/dev/null)
        duplex=$(cat /sys/class/net/$i/duplex 2>/dev/null)
        state=$(cat /sys/class/net/$i/operstate 2>/dev/null)

        if [[ -n $IPv4 ]] || [[ -n $IPv6 ]]; then
            ipRoute=$(ip route | grep 'default' | grep $i 2>/dev/null)
            if [[ -n $ipRoute ]]; then
                gateway=$(echo "$ipRoute"| awk '{print $3}')
                [[ -n $(echo "$ipRoute" | grep "dhcp") ]] && ipType="dynamic" || ipType="static"
            fi
        fi

        if [ "$IPv4" == "$mainIP" ] && [ ${ifaceType,,} == "eth" ]; then
            ifaceType="ethernet"
        fi

        [[ ${macs[*]} =~ $mac ]] && mac="${mac}(duplicate ${#macs[*]})"
        macs+=("$mac")
        network_entry="$i;$iInfo;$ifaceType;$IPv4;$subnet;$IPv6;$prefix;$mac;$speed;$duplex;$state;$gateway;$ipType"
        # shellcheck disable=SC2016
        "$jq_bin" --arg networkEntry "$network_entry" --argjson root "$root" 'getpath($root) += [$networkEntry | split(";") |
            {Name:.[0], Description:.[1], ConnectionType:.[2], IPv4:.[3], Subnet:(.[4]|tonumber? // 0), IPv6:.[5], Prefix:(.[6]|tonumber? // 0),
            MAC:.[7], Speed:.[8], Duplex:.[9], State:.[10], Gateway:.[11], IPType:.[12]} |
            del(..|nulls) | with_entries(select(.value != "")) ]' "$inventory_file" > "${inventory_file%json}tmp" && mv -f "${inventory_file%json}tmp" "$inventory_file"
    done
}

SoundCards() { ### NEED CHECK WITH soundCards SIMULATION
    inventory_file=$1
    parent_root=$2
    subTree="SoundCards"
    # shellcheck disable=SC2016
    "$jq_bin" --argjson root "$parent_root" --arg subTree "$subTree" 'getpath($root) +=  { ($subTree) : []}' "$inventory_file" > "${inventory_file%json}tmp" && mv -f "${inventory_file%json}tmp" "$inventory_file"
    root=""&&root=$(AddToRootPath "$parent_root" "SoundCards")
    
    soundCards=$(arecord -l 2>/dev/null | grep "card ")
    if [[ -n "$soundCards" ]]; then
        IFS=$'\n' read -d '' -r -a sound_cards_arr <<< "$soundCards"
        for sc in "${sound_cards_arr[@]}"; do
            # shellcheck disable=SC2016
            "$jq_bin" --arg sc "$sc" --argjson root "$root" 'getpath($root) += [ {Name:$sc} ]'  "$inventory_file" > "${inventory_file%json}tmp" && mv -f "${inventory_file%json}tmp" "$inventory_file"
        done
    fi
}

InputDevices() {
    inventory_file=$1
    parent_root=$2
    subTree="InputDevices"
    "$jq_bin" --argjson root "$parent_root" --arg subTree "$subTree" 'getpath($root) +=  { ($subTree) : []}' "$inventory_file" > "${inventory_file%json}tmp" && mv -f "${inventory_file%json}tmp" "$inventory_file"
    root=""&&root=$(AddToRootPath "$parent_root" "InputDevices")

    if [[ -d /dev/input/by-id/ ]]; then inputPath="/dev/input/by-id/"
    elif [[ -d /dev/input/by-path/ ]]; then inputPath="/dev/input/by-path/"
    else inputPath="none"
    fi

    if [[ $inputPath != "none" ]]; then
        for i in $(ls -1 $inputPath); do
            inpDevType=""
            if [[ $(echo $i | grep "camera") ]]; then inpDevType="Camera"
            elif [[ $(echo $i | grep "event-kbd") ]]; then inpDevType="Keyboard"
            elif [[ $(echo $i | grep "event-mouse") ]]; then inpDevType="Mouse"
            fi
            [[ -z $inpDevType ]] && break
            inputProperties=$(udevadm info --query=property --name=$inputPath$i)
            for ip in $inputProperties; do
                [[ $(echo $ip | awk -F= '{print $1}') == "ID_MODEL" ]] && inpModel=$(echo $ip | awk -F= '{print $2}') || inpModel=$i
                [[ $(echo $ip | awk -F= '{print $1}') == "ID_VENDOR" ]] && inpVendor=$(echo $ip | awk -F= '{print $2}') || inpVendor="Unknonw"
            done
            id_entry="$inpDevType;$inpModel;$inpVendor"
            # shellcheck disable=SC2016
            "$jq_bin" --arg id_entry "$id_entry"  --argjson root "$root" 'getpath($root) += [$id_entry | split(";") | {Type:.[0], Model:.[1], Vendor:.[2]} |
            del(..|nulls) | with_entries(select(.value != "")) ]' "$inventory_file" > "${inventory_file%json}tmp" && mv -f "${inventory_file%json}tmp" "$inventory_file"
        done
    fi
}

USBDevices() {
    inventory_file=$1
    parent_root=$2
    subTree="USBDevices"
    # shellcheck disable=SC2016
    "$jq_bin" --argjson root "$parent_root" --arg subTree "$subTree" 'getpath($root) +=  { ($subTree) : []}' "$inventory_file" > "${inventory_file%json}tmp" && mv -f "${inventory_file%json}tmp" "$inventory_file"
    root=""&&root=$(AddToRootPath "$parent_root" "USBDevices")

    if [[ $(lsusb > /dev/null 2>&1) ]]; then
        IFS=$'\n' read -d '' -r -a usb_arr <<< "$(lsusb | awk '{OFS=":"}{gsub(/:$/,"",$4)}{print $2,$4}')"
        for deviceId in "${usb_arr[@]}"; do
            vendor=$(lsusb -vs $deviceId 2>/dev/null | grep -m1 idVendor | awk '$1=$2=""; gsub(/^\s+|\s+$/,"",$0)')
            device_info_arr=()
            device=$(lsusb -vs $deviceId 2>/dev/null | grep -m1 ID | awk '{print $6}')
            usbProtocol=$(lsusb -vs $deviceId 2>/dev/null | grep -m1 bInterfaceProtocol | awk '$1=$2=""; gsub(/^\s+|\s+$/,"",$0)')
            usbProduct_1=$(lsusb -vs $deviceId 2>/dev/null | grep -m1 iProduct | awk '$1=$2=""; gsub(/^\s+|\s+$/,"",$0)')
            usbProduct_2=$(lsusb -vs $deviceId 2>/dev/null | grep -m1 idProduct | awk '$1=$2=""; gsub(/^\s+|\s+$/,"",$0)')
            [ -n "$device" ] && device_info_arr+=("$device")
            [ -n "$usbProtocol" ] && device_info_arr+=("$usbProtocol")
            [ -n "$usbProduct_1" ] && device_info_arr+=("$usbProduct_1")
            [ -n "$usbProduct_2" ] && device_info_arr+=("$usbProduct_2")
            device_entry=$(printf " - %s" "${device_info_arr[@]}")
            device_entry=${device_entry:3}
            usb_entry="$vendor;$device_entry"
            # shellcheck disable=SC2016
            "$jq_bin" --arg usbEntry "$usb_entry" --argjson root "$root" 'getpath($root) += [ $usbEntry | split(";") | {Vendor:.[0], Device:.[1]} ]'  "$inventory_file" > "${inventory_file%json}tmp" && mv -f "${inventory_file%json}tmp" "$inventory_file"
        done
    fi
}

PrinterInfo() {   ### NEED CHECK WITH PRINTERS SIMULATION
    inventory_file=$1
    parent_root=$2
    subTree="PrinterInfo"
    "$jq_bin" --argjson root "$parent_root" --arg subTree "$subTree" 'getpath($root) +=  { ($subTree) : []}' "$inventory_file" > "${inventory_file%json}tmp" && mv -f "${inventory_file%json}tmp" "$inventory_file"
    root=""&&root=$(AddToRootPath "$parent_root" "PrinterInfo")

    #[ -n "$(which lpstat 2>/dev/null)" ] && printers=$(lpstat -l -e | awk '{print $1}')
    [ -n "$(which lpstat 2>/dev/null)" ] && printers=$(lpstat -p 2>/dev/null | awk '{print $2}')
    if [[ -n "$printers" ]]; then
        lpstat -e >/dev/null 2>&1
        is_new_lpstat=$?
        defaultPrinter=$(lpq | awk 'NR==1{print $1}')
        for printer in $printers; do
            printerStatus=$(lpstat -a)
            if [[ "$printer" == "$defaultPrinter" ]]; then isDefault="true" ; else isDefault="false" ; fi
            if [[ $(echo "$printerStatus" | grep $printer) ]]; then outStatus="Ready" ; else outStatus="Not Ready" ; fi
            if [ $is_new_lpstat -eq 0 ]; then 
                printerConnect=$(lpstat -l -e | grep $printer | awk '{print $4}')
            else
                printerConnect=$(lpstat -s | grep "$printer:" | awk '{print $4}')
            fi
            printerPath="/etc/cups/ppd/$printer.ppd"
            if [ -f $printerPath ]; then
                printerModel=$(grep ModelName "$printerPath" | awk -F':' '{gsub(/"|^\s+|\s+$/,"",$2); print $2}')
                printerVendor=$(grep Manufacturer "$printerPath" | awk -F':' '{gsub(/"|^\s+|\s+$/,"",$2); print $2}')
            fi
            prn_entry="$printer;$isDefault;$outStatus;$printerConnect;$printerModel;$printerVendor"
            # shellcheck disable=SC2016
            "$jq_bin" --arg prnEntry "$prn_entry"  --argjson root "$root" 'getpath($root) += [$prnEntry | split(";") |
            { Name:.[0], IsDefault:(.[1]|test("true")), Status:.[2], Connection:.[3], Model:.[4], Vendor:.[5]} |
            del(..|nulls) | with_entries(select(.value != "")) ]' "$inventory_file" > "${inventory_file%json}tmp" && mv -f "${inventory_file%json}tmp" "$inventory_file"
        done
    fi
}

UsersInfo() {
    inventory_file=$1
    parent_root=$2
    subTree="UsersInfo"
    "$jq_bin" --argjson root "$parent_root" --arg subTree "$subTree" 'getpath($root) +=  { ($subTree) : []}' "$inventory_file" > "${inventory_file%json}tmp" && mv -f "${inventory_file%json}tmp" "$inventory_file"
    root=""&&root=$(AddToRootPath "$parent_root" "UsersInfo")

    IFS=$'\n' read -d '' -r -a users_arr <<< "$(ls /home)"
    for user in "${users_arr[@]}"; do
        user_entry=$(getent passwd "$user" | awk -F':' -v OFS=';' '{print $1,$6}' )
        # shellcheck disable=SC2016
        "$jq_bin" --arg userEntry "$user_entry"  --argjson root "$root" 'getpath($root) += [ $userEntry | split(";") | {Name:.[0],HomeFolder:.[1]} | 
        del(..|nulls) | with_entries(select(.value != "")) ]' "$inventory_file" > "${inventory_file%json}tmp" && mv -f "${inventory_file%json}tmp" "$inventory_file"
    done
}

MemoryInfo () {
    inventory_file=$1
    parent_root=$2
    subTree="MemoryInfo"
    # shellcheck disable=SC2016
    "$jq_bin" --argjson root "$parent_root" --arg subTree "$subTree" 'getpath($root) +=  { ($subTree) : []}' "$inventory_file" > "${inventory_file%json}tmp" && mv -f "${inventory_file%json}tmp" "$inventory_file"
    root=""&&root=$(AddToRootPath "$parent_root" "MemoryInfo")
    
    #ram_list_json=$("$jq_bin" -n '[]')
    ram_info_raw=$(/usr/sbin/dmidecode -t17 -q | \
    awk 'BEGIN { FS=":"; OFS=";" } /Locator|Manufacturer|Serial Number|Part Number|Size|Channel|Type/ && !/Signature/ { gsub(/^[ \t]+/,"",$1); gsub(/^[ \t]+/,"",$2); line = (line ? line OFS : "") $1 "=" $2 } /^$/ {print line; line=""}')
    IFS=$'\n' read -d '' -r -a ram_info_arr <<< "$(grep -v "No Module Installed" <<< "$ram_info_raw")"
    for ram_info in "${ram_info_arr[@]}"
    do
        ram_info_json=$("$jq_bin" -n '{}')
        name=$(echo "$ram_info" | sed -E -n 's/.*Locator=([^;]*).*/\1/p')
        # shellcheck disable=SC2016
        [ -n "$name" ] && ram_info_json=$("$jq_bin" -n --arg name "$name" --argjson base "$ram_info_json" '$base |. += { ModuleName: $name }')
        manufacturer=$(echo "$ram_info" | sed -E -n 's/.*Vendor=([^;]*).*/\1/p')
        # shellcheck disable=SC2016
        [ -n "$manufacturer" ] && ram_info_json=$("$jq_bin" -n --arg manufacturer "$manufacturer" --argjson base "$ram_info_json" '$base |. += { Vendor: $manufacturer }')
        sn=$(echo "$ram_info" | sed -E -n 's/.*Serial Number=([^;]*).*/\1/p')
        # shellcheck disable=SC2016
        [ -n "$sn" ] && ram_info_json=$("$jq_bin" -n --arg sn "$sn" --argjson base "$ram_info_json" '$base |. += { SerialNumber: $sn }')
        pn=$(echo "$ram_info" | sed -E -n 's/.*Part Number=([^;]*).*/\1/p')
        # shellcheck disable=SC2016
        [ -n "$pn" ] && ram_info_json=$("$jq_bin" -n --arg pn "$pn" --argjson base "$ram_info_json" '$base |. += { PartNumber: $pn }')
        size=$(echo "$ram_info" | sed -E -n 's/.*Size=([^;]*).*/\1/p')
        # shellcheck disable=SC2016
        [ -n "$size" ] && ram_info_json=$("$jq_bin" -n --arg size "$size" --argjson base "$ram_info_json" '$base |. += { Size: $size }')
        channel=$(echo "$ram_info" | sed -E -n 's/.*Channel=([^;]*).*/\1/p')
        # shellcheck disable=SC2016
        [ -n "$channel" ] && ram_info_json=$("$jq_bin" -n --arg channel "$channel" --argjson base "$ram_info_json" '$base |. += { Channel: $channel }')
        type=$(echo "$ram_info" | sed -E -n 's/.*Type=([^;]*).*/\1/p')
        # shellcheck disable=SC2016
        [ -n "$type" ] && ram_info_json=$("$jq_bin" -n --arg type "$type" --argjson base "$ram_info_json" '$base |. += { Type: $type }')

        # shellcheck disable=SC2016
        "$jq_bin" --argjson ramInfo "$ram_info_json"  --argjson root "$root" 'getpath($root) += [$ramInfo]' "$inventory_file" > "${inventory_file%json}tmp" && mv -f "${inventory_file%json}tmp" "$inventory_file"
    done
}

LVInfo(){
    inventory_file=$1
    parent_root=$2
    subTree="LVInfo"
    # shellcheck disable=SC2016
    "$jq_bin" --argjson root "$parent_root" --arg subTree "$subTree" 'getpath($root) +=  { ($subTree) : []}' "$inventory_file" > "${inventory_file%json}tmp" && mv -f "${inventory_file%json}tmp" "$inventory_file"
    root=""&&root=$(AddToRootPath "$parent_root" "LVInfo")

    if [[ $(LVM_SUPPRESS_FD_WARNINGS=1 lvdisplay -c 2>/dev/null) ]]; then    
        lvInfo=$(LVM_SUPPRESS_FD_WARNINGS=1  lvdisplay -c)
        for lv in $lvInfo; do
            lv_entry=$(echo "$lv" | awk -F':' '{print $1";"$7";"$2}')
            # shellcheck disable=SC2016
            "$jq_bin" --arg lvEntry "$lv_entry"  --argjson root "$root" 'getpath($root) += [ $lvEntry | split(";") | {Name:.[0], Size:(.[1]|tonumber? // 0), VolumeGroupName:.[2]} | 
            del(..|nulls) | with_entries(select(.value != "")) ]' "$inventory_file" > "${inventory_file%json}tmp" && mv -f "${inventory_file%json}tmp" "$inventory_file"
        done
    fi
}

VGInfo(){
    inventory_file=$1
    parent_root=$2
    subTree="VGInfo"
    # shellcheck disable=SC2016
    "$jq_bin" --argjson root "$parent_root" --arg subTree "$subTree" 'getpath($root) +=  { ($subTree) : []}' "$inventory_file" > "${inventory_file%json}tmp" && mv -f "${inventory_file%json}tmp" "$inventory_file"
    root=""&&root=$(AddToRootPath "$parent_root" "VGInfo")

    if [[ $(LVM_SUPPRESS_FD_WARNINGS=1 vgdisplay -c 2>/dev/null) ]]; then
        vgInfo=$(LVM_SUPPRESS_FD_WARNINGS=1 vgdisplay -c)
        for vg in $vgInfo; do
            vg_entry=$(echo "$vg" | awk -F':' '{print $1";"$12}')
            # shellcheck disable=SC2016
            "$jq_bin" --arg vgEntry "$vg_entry"  --argjson root "$root" 'getpath($root) += [ $vgEntry | split(";") | {Name:.[0], Size:(.[1]|tonumber? // 0)} | 
            del(..|nulls) | with_entries(select(.value != "")) ]' "$inventory_file" > "${inventory_file%json}tmp" && mv -f "${inventory_file%json}tmp" "$inventory_file"
        done
    fi
}

PVInfo(){
    inventory_file=$1
    parent_root=$2
    subTree="PVInfo"
    # shellcheck disable=SC2016
    "$jq_bin" --argjson root "$parent_root" --arg subTree "$subTree" 'getpath($root) +=  { ($subTree) : []}' "$inventory_file" > "${inventory_file%json}tmp" && mv -f "${inventory_file%json}tmp" "$inventory_file"
    root=""&&root=$(AddToRootPath "$parent_root" "PVInfo")

    if [[ $(LVM_SUPPRESS_FD_WARNINGS=1 LVM_SUPPRESS_FD_WARNINGS=1 pvdisplay -c 2>/dev/null) ]]; then
        pvInfo=$(LVM_SUPPRESS_FD_WARNINGS=1 pvdisplay -c)
        for pv in $pvInfo; do
            pv_entry=$(echo "$pv" | awk -F':' '{print $1";"$2";"$3}')
            # shellcheck disable=SC2016
            "$jq_bin" --arg pvEntry "$pv_entry"  --argjson root "$root" 'getpath($root) += [ $pvEntry | split(";") | {Name:.[0], VolumeGroupName:.[1], Size:(.[2]|tonumber? // 0)} | 
            del(..|nulls) | with_entries(select(.value != "")) ]' "$inventory_file" > "${inventory_file%json}tmp" && mv -f "${inventory_file%json}tmp" "$inventory_file"
        done
    fi
}

MotherboardInfo(){
    inventory_file=$1
    parent_root=$2
    subTree="MotherboardInfo"
    # shellcheck disable=SC2016
    "$jq_bin" --argjson root "$parent_root" --arg subTree "$subTree" 'getpath($root) +=  { ($subTree) : {}}' "$inventory_file" > "${inventory_file%json}tmp" && mv -f "${inventory_file%json}tmp" "$inventory_file"
    root=""&&root=$(AddToRootPath "$parent_root" "MotherboardInfo")

    mbVendor=$(/usr/sbin/dmidecode --string baseboard-manufacturer)
    mbName=$(/usr/sbin/dmidecode --string baseboard-product-name)
    mbSN=$(/usr/sbin/dmidecode --string baseboard-serial-number)
    mbVer=$(/usr/sbin/dmidecode --string baseboard-version)
    mb_info="$mbVendor;$mbName;$mbSN;$mbVer"
    # shellcheck disable=SC2016
    "$jq_bin" --arg mbInfo "$mb_info" --argjson root "$root" 'getpath($root) += ($mbInfo | split(";") | {Manufacturer:.[0], Name:.[1], SerialNumber:.[2], Version:.[3]} |del(..|nulls) | with_entries(select(.value != "")) )' "$inventory_file" > "${inventory_file%json}tmp" && mv -f "${inventory_file%json}tmp" "$inventory_file"
}

Drives() {
    inventory_file=$1
    parent_root=$2
    subTree="Drives"
    "$jq_bin" --argjson root "$parent_root" --arg subTree "$subTree" 'getpath($root) +=  { ($subTree) : []}' "$inventory_file" > "${inventory_file%json}tmp" && mv -f "${inventory_file%json}tmp" "$inventory_file"
    root=""&&root=$(AddToRootPath "$parent_root" "Drives")

    for drive in $driveNames; do 
        drvSize=$(lsblk -brn | grep -w "$drive" | awk '{print int($4/1024)}')
        drvPath="/sys/class/block/$drive"

        if [ "$drive" == "$bootDrv" ]; then bootFlag="true"; else bootFlag="false"; fi
        if [[ $(cat "$drvPath/removable") == "1" ]]; then drvType="Removable"
        elif [[ $(cat "$drvPath/queue/rotational") == "0" ]]; then drvType="SSD"
        else drvType="HDD"
        fi

        [ -f "$drvPath/device/model" ] && drvModel=$(cat "$drvPath/device/model" | tr -d " ") || drvModel=""
        [ -f "$drvPath/device/vendor" ] && drvVendor=$(cat "$drvPath/device/vendor" | tr -d " ") || drvVendor=""
        drvSerial=$(lsblk -n --nodeps --output NAME,SERIAL | grep "$drive" | awk '{print $2}')
        drvIface=$(lsblk -n --nodeps --output NAME,TRAN | grep "$drive" | awk '{print $2}')
        drvFW=$(udevadm info --query all --name="/dev/$drive" | grep ID_REVISION | awk -F'=' '{print $2}')

        drv_entry="$drive;$drvSize;$drvType;$bootFlag;$drvModel;$drvVendor;$drvSerial;$drvIface;$drvFW"
        # shellcheck disable=SC2016
        "$jq_bin" --arg drvEntry "$drv_entry"  --argjson root "$root" 'getpath($root) += [ $drvEntry | split(";") | { Name:.[0], Size:(.[1]|tonumber? // .), Type:.[2], Boot:(.[3]|test("true")), Model:.[4], Vendor:.[5], SerialNumber:.[6], Interface:.[7], Firmware:.[8]} | 
            del(..|nulls) | with_entries(select(.value != "")) ]' "$inventory_file" > "${inventory_file%json}tmp" && mv -f "${inventory_file%json}tmp" "$inventory_file"
    done
}

Partitions(){  ### cheeck to need zeroed partition free space var
    inventory_file=$1
    parent_root=$2
    subTree="Partitions"
    "$jq_bin" --argjson root "$parent_root" --arg subTree "$subTree" 'getpath($root) +=  { ($subTree) : []}' "$inventory_file" > "${inventory_file%json}tmp" && mv -f "${inventory_file%json}tmp" "$inventory_file"
    root=""&&root=$(AddToRootPath "$parent_root" "Partitions")

    ptrNames=$(lsblk -nr | awk '{if($6=="part") print $1}')
    for prt in $ptrNames; do
        prtSize=$(lsblk -brn | grep -w "$prt" | awk '{print int(($4/1024)+0.5)}')
        prtFS=$(lsblk -n --output NAME,FSTYPE | grep "$prt" | awk '{print $2}')
        prtMntP=$(lsblk -n --output NAME,MOUNTPOINT | grep "$prt" | awk '{print $2}')
        prtFreeSp=$(df | grep $prt | awk '{print $4}' | head -n1)

        prt_entry="$prt;$prtSize;part;$prtFS;$prtMntP;$prtFreeSp"
        # shellcheck disable=SC2016
        "$jq_bin" --arg prtEntry "$prt_entry"  --argjson root "$root" 'getpath($root) += [ $prtEntry | split(";") | { Name:.[0], Size:(.[1]|tonumber? // 0), Type:.[2], FileSystem:.[3], MountPoint:.[4], FreeSpace:(.[5]|tonumber? // 0)} | 
            del(..|nulls) | with_entries(select(.value != "")) ]' "$inventory_file" > "${inventory_file%json}tmp" && mv -f "${inventory_file%json}tmp" "$inventory_file"
    done
}

Smart(){
    inventory_file=$1
    parent_root=$2
    subTree="SMARTInfo"
    "$jq_bin" --argjson root "$parent_root" --arg subTree "$subTree" 'getpath($root) +=  { ($subTree) : []}' "$inventory_file" > "${inventory_file%json}tmp" && mv -f "${inventory_file%json}tmp" "$inventory_file"
    root=""&&root=$(AddToRootPath "$parent_root" "SMARTInfo")

    smartctl -V 2&> /dev/null
    smartctl_status=$?
    if [[ $smartctl_status -eq 0 ]]; then
        for drive in $driveNames; do
            if [[ $(cat "/sys/class/block/$drive/removable") == "0" ]]; then
                smrtStatus=$(smartctl -i "/dev/$drive" | grep SMART | tail -1 | awk -F': ' '{print $2}')
                if [[ $smrtStatus == "Enabled" ]]; then 
                    testRes=$(smartctl -a "/dev/$drive" | grep "SMART overall-health self-assessment test" | awk -F':' '{sub(/ /,"",$2); print $2}')
                    rawReadErrRate=$(smartctl -a "/dev/$drive" | grep "Raw_Read_Error_Rate" | awk '{print $10}')
                    spinUpTime=$(smartctl -a "/dev/$drive" | grep "Spin_Up_Time" | awk '{print $10}')
                    rlctdSectorCt=$(smartctl -a "/dev/$drive" | grep "Reallocated_Sector_Ct" | awk '{print $10}')
                    seekErrRate=$(smartctl -a "/dev/$drive" | grep "Seek_Error_Rate" | awk '{print $10}')
                    seekTimePrfmnc=$(smartctl -a "/dev/$drive" | grep "Seek_Time_Performance" | awk '{print $10}')
                    spinRetryCt=$(smartctl -a "/dev/$drive" | grep "Spin_Retry_Count" | awk '{print $10}')
                    wearLlvlngCt=$(smartctl -a "/dev/$drive" | grep "Wear_Leveling_Count" | awk '{print $10}')
                    usedRsvdBlkCntChip=$(smartctl -a "/dev/$drive" | grep "Used_Rsvd_Blk_Cnt_Chip" | awk '{print $10}')
                    unusedRsvdBlkCntTot=$(smartctl -a "/dev/$drive" | grep "Unused_Rsvd_Blk_Cnt_Tot" | awk '{print $10}')
                    e2err=$(smartctl -a "/dev/$drive" | grep "End-to-End_Error" | awk '{print $10}')
                    mediaWearoutInd=$(smartctl -a "/dev/$drive" | grep "Media_Wearout_Indicator" | awk '{print $10}')

                    smart_entry="$drive;$smrtStatus;$testRes;$rawReadErrRate;$spinUpTime;$rlctdSectorCt;$seekErrRate;$seekTimePrfmnc;$spinRetryCt;$wearLlvlngCt;$usedRsvdBlkCntChip;$unusedRsvdBlkCntTot;$e2err;$mediaWearoutInd;"
                    # shellcheck disable=SC2016
                    "$jq_bin" --arg smartEntry "$smart_entry"  --argjson root "$root" 'getpath($root) += [ $smartEntry | split(";") | {
                                        DiskName: .[0], Status: .[1], TestResult: .[2],RawReadErrorRate: (.[3]|tonumber? // 0),
                                        SpinUpTime: (.[4]|tonumber? // 0),ReallocatedSectorCt: (.[5]|tonumber? // 0),
                                        SeekErrorRate: (.[6]|tonumber? // 0),SeekTimePerformance: .[7], SpinRetryCount: (.[8]|tonumber? // 0), WearLevelingCount: (.[9]|tonumber? // 0), UsedRsvdBlkCntChip: (.[10]|tonumber? // 0),
                                        EndToEndError: (.[12]|tonumber? // 0),UnusedRsvdBlkCntTotal: (.[11]|tonumber? // 0),MediaWearoutIndicator: (.[13]|tonumber? // 0) } | 
                                        del(..|nulls) | with_entries(select(.value != "")) ]' "$inventory_file" > "${inventory_file%json}tmp" && mv -f "${inventory_file%json}tmp" "$inventory_file"
                fi
            fi
        done
        # "$jq_bin" -R '{
        #     SMARTInfo: [inputs|split(";") | {
        #         DiskName: .[0], Status: .[1], TestResult: .[2],
        #         RawReadErrorRate: (if .[3] == "" then null else .[3] | tonumber end),
        #         SpinUpTime: (if .[4] == "" then null else .[4] | tonumber end),
        #         ReallocatedSectorCt: (if .[5] == "" then null else .[5] | tonumber end),
        #         SeekErrorRate: (if .[6] == "" then null else .[6] | tonumber end),
        #         SeekTimePerformance: .[7], SpinRetryCount: .[8], WearLevelingCount: .[9], UsedRsvdBlkCntChip: .[10],
        #         EndToEndError: (if .[12] == "" then null else .[12] | tonumber end),
        #         UnusedRsvdBlkCntTotal: (if .[11] == "" then null else .[11] | tonumber end), 
        #         MediaWearoutIndicator: (if .[13] == "" then null else .[13] | tonumber end), } |
        #         with_entries(select(.value != "" and .value != null))
        #     ]}' smart.info
    fi
}

RAIDInfo() {
    inventory_file=$1
    parent_root=$2
    subTree="RAIDInfo"
    # shellcheck disable=SC2016
    "$jq_bin" --argjson root "$parent_root" --arg subTree "$subTree" 'getpath($root) +=  { ($subTree) : []}' "$inventory_file" > "${inventory_file%json}tmp" && mv -f "${inventory_file%json}tmp" "$inventory_file"
    root=""&&root=$(AddToRootPath "$parent_root" "RAIDInfo")

    raidPath="/proc/mdstat"
    if [ -f "$raidPath" ]; then
        if [[ $(grep "Personalities" "$raidPath" | awk -F': ' '{print $2}') == "\n" ]]; then
            raidStatus="Not enabled"
        else
            raidStatus="Enabled"
            raidType=$(grep "Personalities" "$raidPath" | awk -F': ' 'sub(/ /,"",$2); {print $2}')
        fi
        if [[ $(grep "md\|blocks" "$raidPath") ]]; then
            for raid in $(grep "md" "$raidPath" | awk -F' : ' '{print $1};'); do
                raidState=$(grep -A1 "$raid" "$raidPath" | grep -v "$raid" | sed 's/^ *//')
                raid_entry="${raid};${raidState}"
                # shellcheck disable=SC2016
                "$jq_bin" --arg raidEntry "$raid_entry"  --argjson root "$root" 'getpath($root) += [ $raidEntry | split(";") | { Name:.[0], State:.[1] } | 
                        del(..|nulls) | with_entries(select(.value != "")) ]' "$inventory_file" > "${inventory_file%json}tmp" && mv -f "${inventory_file%json}tmp" "$inventory_file"
            done
        fi
    fi
}

DeviceInventory() {
    inventory_file=$1
    parent_root=$2
    subTree="DeviceInventory"
    # shellcheck disable=SC2016
    "$jq_bin" --argjson root "$parent_root" --arg subTree "$subTree" 'getpath($root) +=  { ($subTree) : {} }' "$inventory_file" > "${inventory_file%json}tmp" && mv -f "${inventory_file%json}tmp" "$inventory_file"
    di_root=$(AddToRootPath "$parent_root" "DeviceInventory")

    OSInfo "$inventory_file" "$di_root"
    # shellcheck disable=SC2016
    "$jq_bin" --arg Domain "$domain" --argjson root "$di_root" 'getpath($root) += {"Domain": $Domain}' "$outFile" > "${outFile%json}tmp" && mv -f "${outFile%json}tmp" "$outFile"
    # shellcheck disable=SC2016
    "$jq_bin" --arg Hostname "$hostname" --argjson root "$di_root" 'getpath($root) += {"Hostname": $Hostname}' "$outFile" > "${outFile%json}tmp" && mv -f "${outFile%json}tmp" "$outFile"
    # shellcheck disable=SC2016
    "$jq_bin" --arg FQDN "$fqdn" --argjson root "$di_root" 'getpath($root) += {"FQDN": $FQDN}' "$outFile" > "${outFile%json}tmp" && mv -f "${outFile%json}tmp" "$outFile"
    BIOSInfo "$inventory_file" "$di_root"
    GPUInfo "$inventory_file" "$di_root"
    DisplayInfo "$inventory_file" "$di_root"
    # shellcheck disable=SC2016
    "$jq_bin" --arg CPUCount "$cpuCount" --argjson root "$di_root" 'getpath($root) += {"CPUCount": ($CPUCount|tonumber? // 0)}' "$outFile" > "${outFile%json}tmp" && mv -f "${outFile%json}tmp" "$outFile"
    CPUInfo "$inventory_file" "$di_root"
    PCIDevices "$inventory_file" "$di_root"
    ProxySettings "$inventory_file" "$di_root"
    AutoStartApplications "$inventory_file" "$di_root"
    NetworkAdapters "$inventory_file" "$di_root" "$mainIP"
    SoundCards "$inventory_file" "$di_root"
    InputDevices "$inventory_file" "$di_root"
    USBDevices "$inventory_file" "$di_root"
    PrinterInfo "$inventory_file" "$di_root"
    UsersInfo "$inventory_file" "$di_root"
    # shellcheck disable=SC2016
    "$jq_bin" --arg Model "$model" --argjson root "$di_root" 'getpath($root) += {"Model": $Model}' "$outFile" > "${outFile%json}tmp" && mv -f "${outFile%json}tmp" "$outFile"
    # shellcheck disable=SC2016
    "$jq_bin" --arg Manufacturer "$di_manufacturer" --argjson root "$di_root" 'getpath($root) += {"Manufacturer": $Manufacturer}' "$outFile" > "${outFile%json}tmp" && mv -f "${outFile%json}tmp" "$outFile"
    # shellcheck disable=SC2016
    "$jq_bin" --arg LastLogonUser "$lastLogonUser" --argjson root "$di_root" 'getpath($root) += {"LastLogonUser": $LastLogonUser}' "$outFile" > "${outFile%json}tmp" && mv -f "${outFile%json}tmp" "$outFile"
    # shellcheck disable=SC2016
    "$jq_bin" --arg LastLogonTime "$lastLogonTime" --argjson root "$di_root" 'getpath($root) += {"LastLogonTime": $LastLogonTime}' "$outFile" > "${outFile%json}tmp" && mv -f "${outFile%json}tmp" "$outFile"
    # shellcheck disable=SC2016
    "$jq_bin" --arg LastRebootTime "$lastRebootTime" --argjson root "$di_root" 'getpath($root) += {"LastRebootTime": $LastRebootTime}' "$outFile" > "${outFile%json}tmp" && mv -f "${outFile%json}tmp" "$outFile"
    # shellcheck disable=SC2016
    "$jq_bin" --arg PhysicalMemory "$physicalMemory" --argjson root "$di_root" 'getpath($root) += {"PhysicalMemory": ($PhysicalMemory|tonumber? // 0)}' "$outFile" > "${outFile%json}tmp" && mv -f "${outFile%json}tmp" "$outFile"
    MemoryInfo "$inventory_file" "$di_root"
    # shellcheck disable=SC2016
    "$jq_bin" --arg MemorySlots "$memSlots" --argjson root "$di_root" 'getpath($root) += {"MemorySlots": ($MemorySlots|tonumber? // 0)}' "$outFile" > "${outFile%json}tmp" && mv -f "${outFile%json}tmp" "$outFile"
    # shellcheck disable=SC2016
    "$jq_bin" --arg MemoryModules "$memModules" --argjson root "$di_root" 'getpath($root) += {"MemoryModules": ($MemoryModules|tonumber? // 0)}' "$outFile" > "${outFile%json}tmp" && mv -f "${outFile%json}tmp" "$outFile"
    Drives "$inventory_file" "$di_root"
    Partitions "$inventory_file" "$di_root"
    Smart "$inventory_file" "$di_root"
    RAIDInfo "$inventory_file" "$di_root"
    # shellcheck disable=SC2016
    "$jq_bin" --arg RAIDStatus "$raidStatus" --argjson root "$di_root" 'getpath($root) += {"RAIDStatus": $RAIDStatus}' "$outFile" > "${outFile%json}tmp" && mv -f "${outFile%json}tmp" "$outFile"
    # shellcheck disable=SC2016
    "$jq_bin" --arg RAIDType "$raidType" --argjson root "$di_root" 'getpath($root) += {"RAIDType": $RAIDType}' "$outFile" > "${outFile%json}tmp" && mv -f "${outFile%json}tmp" "$outFile"
    # shellcheck disable=SC2016
    "$jq_bin" --arg DefaultBrowser "$defBrowser" --argjson root "$di_root" 'getpath($root) += {"DefaultBrowser": $DefaultBrowser}' "$outFile" > "${outFile%json}tmp" && mv -f "${outFile%json}tmp" "$outFile"
    # shellcheck disable=SC2016
    "$jq_bin" --arg PowerPlan "$powerPlan" --argjson root "$di_root" 'getpath($root) += {"PowerPlan": $PowerPlan}' "$outFile" > "${outFile%json}tmp" && mv -f "${outFile%json}tmp" "$outFile"
    # shellcheck disable=SC2016
    "$jq_bin" --arg FirewallStatus "$fwStatus" --argjson root "$di_root" 'getpath($root) += {"FirewallStatus": $FirewallStatus}' "$outFile" > "${outFile%json}tmp" && mv -f "${outFile%json}tmp" "$outFile"
    # shellcheck disable=SC2016
    "$jq_bin" --arg FirewallName "$fwName" --argjson root "$di_root" 'getpath($root) += {"FirewallName": $FirewallName}' "$outFile" > "${outFile%json}tmp" && mv -f "${outFile%json}tmp" "$outFile"
    # shellcheck disable=SC2016
    "$jq_bin" --arg DNSServers "$dnsSrvs" --argjson root "$di_root" 'getpath($root) +=  {"DNSServers": $DNSServers}' "$outFile" > "${outFile%json}tmp" && mv -f "${outFile%json}tmp" "$outFile"
    # shellcheck disable=SC2016
    "$jq_bin" --arg MainIPAddress "$mainIP" --argjson root "$di_root" 'getpath($root) += {"MainIPAddress": $MainIPAddress}' "$outFile" > "${outFile%json}tmp" && mv -f "${outFile%json}tmp" "$outFile"
    # shellcheck disable=SC2016
    "$jq_bin" --arg Bootloader "$bootloader" --argjson root "$di_root" 'getpath($root) += {"Bootloader": $Bootloader}' "$outFile" > "${outFile%json}tmp" && mv -f "${outFile%json}tmp" "$outFile"
    # shellcheck disable=SC2016
    "$jq_bin" --arg DesktopEnvironment "$desktopEnv" --argjson root "$di_root" 'getpath($root) += {"DesktopEnvironment": $DesktopEnvironment}' "$outFile" > "${outFile%json}tmp" && mv -f "${outFile%json}tmp" "$outFile"
    MotherboardInfo "$inventory_file" "$di_root"
    # shellcheck disable=SC2016
    "$jq_bin" --arg LVMStatus "$lvmStatus" --argjson root "$di_root" 'getpath($root) += {"LVMStatus": $LVMStatus}' "$outFile" > "${outFile%json}tmp" && mv -f "${outFile%json}tmp" "$outFile"
    LVInfo "$inventory_file" "$di_root"
    VGInfo "$inventory_file" "$di_root"
    PVInfo "$inventory_file" "$di_root"
    # shellcheck disable=SC2016
    "$jq_bin" --arg PCSystemType "$psSysType" --argjson root "$di_root" 'getpath($root) += {"PCSystemType": $PCSystemType}' "$outFile" > "${outFile%json}tmp" && mv -f "${outFile%json}tmp" "$outFile"
}

InventoryOut() {
    "$jq_bin" -n '{}' > "$outFile"
    inventory_root='[]'
    # shellcheck disable=SC2016
    "$jq_bin" --arg Timestamp "$timestamp" --argjson root "$inventory_root" 'getpath($root) += {"Timestamp": $Timestamp}' "$outFile" > "${outFile%json}tmp" && mv -f "${outFile%json}tmp" "$outFile"
    AppInventory "$outFile" "$inventory_root"
    ServiceStateInventory "$outFile" "$inventory_root"
    DeviceInventory "$outFile" "$inventory_root"
    if [ -n "$meshAgentId" ]; then
        # shellcheck disable=SC2016
        "$jq_bin" --arg MeshAgentId "$meshAgentId" --argjson root "$inventory_root" 'getpath($root) += {"MeshAgentId": $MeshAgentId}' "$outFile" > "${outFile%json}tmp" && mv -f "${outFile%json}tmp" "$outFile"
    fi
}

InventoryOut
