#!/bin/bash
# Copyright (C) 2024 MOXA Inc. All rights reserved.
# This software is distributed under the terms of the MOXA SOFTWARE NOTICE.
# See the file LICENSE for details.
#
# Name:
#       MOXA Common Library
# Authors:
#       2024    Wilson Huang <WilsonYS.Huang@moxa.com>
#

CONFIG_FILE="/var/lib/moxa/mx-io-state.conf"

function thread_start() {
    local fn="$1"; shift
    if ! declare -F "$fn" >/dev/null; then
      echo "thread_start: function not found: $fn" >&2
      return 1
    fi

    (
        trap 'exit 0' TERM INT
        "$fn" "$@"
    ) &
}

function thread_stop() {
    local pid
    for pid in "$@"; do
        kill -TERM "$pid" 2>/dev/null || true
    done
}

function thread_join() {
    local pid
    for pid in "$@"; do
        wait "$pid" 2>/dev/null || true
    done
}

function thread_stop_wait() {
    thread_stop "$@"
    thread_join "$@"
}

function is_module_loaded() {
        local mod_name
        mod_name=${1}

        lsmod | grep -w $mod_name &>/dev/null
}

function check_leading_zero_digit() {
        local digit
        digit=${1}

        [[ "$digit" =~ ^([1-9][0-9]*|0)$ ]]
}

function get_model_name_from_dmi_type12() {
        /usr/sbin/dmidecode -t 12 |
                grep "Option " |
                awk -F ':' '{print substr($2,1,11)}' |
                sed 's/ //g'
}

function get_model_name_from_dmi_type1() {
        /usr/sbin/dmidecode -t 1 |
                grep "Product Name" |
                awk -F ':' '{print $2}' |
                sed 's/ //g'
}

function bind_i2c_driver() {
		local master_device_name=$1
		local slave_device_name=$2
		shift 2
		local reg_addr=$@
		local i2c_num=0
		local ret=0

		for filename in /sys/bus/i2c/devices/i2c-*/name; do
				i2c_devname=$(cat ${filename})
				if [[ $i2c_devname == *"$master_device_name"* ]]; then
						i2c_devpath=$(echo ${filename%/*})
                        i2c_num=$(echo ${i2c_devpath} | cut -d '-' -f2)
						for addr in $reg_addr; do
								if [[ ! -e "${i2c_devpath}/${i2c_num}-00${addr##0x}" ]]; then
										echo "$slave_device_name $addr" > ${i2c_devpath}/new_device
										[[ $? -ne 0 ]] && ret=1
								fi
						done
				fi
		done

		echo $ret
}

function get_i2c_device_minor_number_list() {
		local i2c_device_name=$1
		local i2c_num=0
		local i2c_nums=()

		for filename in /sys/bus/i2c/devices/i2c-*/name; do
				i2c_devname=$(cat ${filename})
				if [[ $i2c_devname == *"$i2c_device_name"* ]]; then
						i2c_devpath=$(echo ${filename%/*})
                        i2c_num=$(echo ${i2c_devpath} | cut -d '-' -f2)
						i2c_nums+=($i2c_num)
				fi
		done

		echo "${i2c_nums[@]}"
}

function format_as_i2c_directory_name() {
		# Input example: i2c_number(e.g. 0, 1, 2, 3, ...) and device_address(e.g. 0x26, 0x27, ...)
		# Output example: 4-0026, 4-0027 ...

		local i2c_number=$1
		local device_address=$2

		echo "$i2c_number-00${device_address##0x}"
}

function i2c_read() {
    local bus=$1
    local chip_addr=$2
    local data_addr=$3
    local val

    if [ -z "$bus" ] || [ -z "$chip_addr" ] || [ -z "$data_addr" ]; then
        return 1
    fi

    val=$(/usr/sbin/i2cget -f -y "$bus" "$chip_addr" "$data_addr" 2>&1)

    if [ $? -eq 0 ]; then
        echo "$val"
        return 0
    else
        return 1
    fi
}

function get_board_index_from_eeprom() {
		local address=$1
		local offset=$2

		local i2c_num=$(/usr/sbin/i2cdetect -l | grep smbus | awk -F ' ' '{print $1}' | awk -F '-' '{print $2}')

		/usr/sbin/i2cget -f -y $i2c_num $address $offset 2>/dev/null
}

# function to set bit
function set_bit() {
        local action=$1
        local value=$2
        local index=$3
        local mask=$((1 << index))
        local result

        case "$action" in
                clear)   result=$(( value & ~mask )) ;;
                set)     result=$(( value | mask )) ;;
                toggle)  result=$(( value ^ mask )) ;;
                *)       echo "Error: invalid action: $action" >&2; return 1 ;;
        esac

        printf "%x" "$result"
}

function save_to_config() {
        local tool="$1"
        local port_str="$2"
        local args_str="$3"
        local tmp_file

        # Ensure directory exists
        mkdir -p "$(dirname "$CONFIG_FILE")"

        # Initialize file with column headers if it doesn't exist
        if [[ ! -f "$CONFIG_FILE" ]]; then
                echo "# Tool          Port/Slot  Args" > "$CONFIG_FILE"
        fi

        tmp_file=$(mktemp)

        # Use awk to dynamically match the Tool ($1) and Port/Slot ($2 + $3)
        awk -v t="$tool" -v p="$port_str" -v a="$args_str" '
        BEGIN { found = 0 }
        /^#/ { print; next }

        # Match the tool name and reconstruct the port string (e.g., "-p 1")
        $1 == t && ($2 " " $3) == p {
                printf "%-15s %-10s %s\n", t, p, a
                found = 1
                next
        }

        # Pass through non-matching lines
        { print }

        # Append if the exact tool and port combination was not found
        END {
                if (!found) {
                        printf "%-15s %-10s %s\n", t, p, a
                }
        }
        ' "$CONFIG_FILE" > "$tmp_file"

        # Safely overwrite preserving inodes/permissions
        cat "$tmp_file" > "$CONFIG_FILE"
        rm -f "$tmp_file"
        chmod 644 "$CONFIG_FILE"
}
