#!/usr/bin/env bash

# Copyright (c) 2024 Jordi Marqués
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

declare -g -a __return_array
declare -g __return
declare -g __rl_battery_name
declare -g __rl_machine
declare -g __rl_hidraw_device
declare -g __rl_input_device_keyboard
declare -g -a machines
declare -g -A machine_hidraws
declare -g -A machine_input_device_keyboards
declare -g -A machine_battery_names

# shellcheck disable=SC2034
machines=(
    "MNT Pocket Reform with BPI-CM4 Module"
    "MNT Pocket Reform with RCORE RK3588 Module"
    "MNT Pocket Reform with i.MX8MP Module"
    # "MNT Reform 2 HDMI"
    # "MNT Reform 2 with BPI-CM4 Module"
    # "MNT Reform 2 with LS1028A Module"
    # "MNT Reform 2 with RCORE RK3588 Module"
    # "MNT Reform 2 with i.MX8MP Module"
    # "MNT Reform 2"
    # "MNT Reform Next with RCORE RK3588 Module"
)

machine_hidraws=(
    # <2025-01-01 firmware: "MNT Pocket Reform Input"
    # >2025-01-01 firmware: "MNT Research Pocket Reform Input 1.0"
    ["MNT Pocket Reform with BPI-CM4 Module"]="MNT( Research)? Pocket Reform Input( 1[.]0)?"
    ["MNT Pocket Reform with RCORE RK3588 Module"]="MNT( Research)? Pocket Reform Input( 1[.]0)?"
    ["MNT Pocket Reform with i.MX8MP Module"]="MNT( Research)? Pocket Reform Input( 1[.]0)?"
    # "MNT Reform 2 HDMI"
    # "MNT Reform 2 with BPI-CM4 Module"
    # "MNT Reform 2 with LS1028A Module"
    # "MNT Reform 2 with RCORE RK3588 Module"
    # "MNT Reform 2 with i.MX8MP Module"
    # "MNT Reform 2"
    # "MNT Reform Next with RCORE RK3588 Module"
)

machine_input_device_keyboards=(
    ["MNT Pocket Reform with BPI-CM4 Module"]="usb-MNT_(Research_)?Pocket_Reform_Input_(.*)-event-kbd"
    ["MNT Pocket Reform with RCORE RK3588 Module"]="usb-MNT_(Research_)?Pocket_Reform_Input_(.*)-event-kbd"
    ["MNT Pocket Reform with i.MX8MP Module"]="usb-MNT_(Research_)?Pocket_Reform_Input_(.*)-event-kbd"
    # "MNT Reform 2 HDMI"]=""
    # "MNT Reform 2 with BPI-CM4 Module"]=""
    # "MNT Reform 2 with LS1028A Module"]=""
    # "MNT Reform 2 with RCORE RK3588 Module"]=""
    # "MNT Reform 2 with i.MX8MP Module"]=""
    # "MNT Reform 2"]=""
    # "MNT Reform Next with RCORE RK3588 Module"]=""
)

machine_battery_names=(
    ["MNT Pocket Reform with BPI-CM4 Module"]="BAT0"
    ["MNT Pocket Reform with RCORE RK3588 Module"]="BAT0"
    ["MNT Pocket Reform with i.MX8MP Module"]="BAT0"
    # ["MNT Reform 2 HDMI"]=""
    # ["MNT Reform 2 with BPI-CM4 Module"]=""
    # ["MNT Reform 2 with LS1028A Module"]=""
    # ["MNT Reform 2 with RCORE RK3588 Module"]=""
    # ["MNT Reform 2 with i.MX8MP Module"]=""
    # ["MNT Reform 2"]=""
    # ["MNT Reform Next with RCORE RK3588 Module"]=""
)

machine_get() {
    # Return the machine on __return variable.
    #
    # Examples:
    #     "MNT Pocket Reform with i.MX8MP Module"
    local text
    if [[ -n "${__rl_machine:-}" ]]; then
        true # cached
    else
        read -r -d "" text </proc/device-tree/model
        __rl_machine="${text}"
    fi
    __return="${__rl_machine}"
}

hidraw_device_get() {
    # Return the name of the hidraw device under /dev or returns exit code 1.
    # This function is cached.
    #
    # Example:
    #     set __return to "hidraw0"
    local device
    local machine
    local text

    if [[ -n "${__rl_hidraw_device:-}" ]]; then
        __return="${__rl_hidraw_device}" # cached
        return 0
    fi

    machine_get && machine="${__return}"

    while true; do
        if [[ -d /sys/class/hidraw ]]; then
            cd /sys/class/hidraw || return 1
            for device in *; do
                if [[ -f "${device}/device/uevent" ]]; then
                    readarray text <"${device}/device/uevent"
                    if [[ "${text[*]}" =~ ${machine_hidraws["${machine}"]} ]]; then
                        cd - >/dev/null || return 1
                        __rl_hidraw_device="/dev/${device}"
                        __return="/dev/${device}"
                        return 0
                    fi
                fi
            done
            cd - >/dev/null || return 1

        fi
        echo "Waiting for hidraw device to be up ..."
        sleep 1
    done
}

hidraw_leds_set() {
    # Set keyboard leds colour.
    #
    # Args:
    #     r(int): green colour, range 0-255.
    #     g(int): green colour, range 0-255.
    #     b(int): green colour, range 0-255.
    #
    # Example:
    #     hidraw_leds_set 10 0 255
    local hidraw_device
    local r g b
    hidraw_device_get && hidraw_device="${__return}"
    # Get hexadecimal from decimal
    printf -v r "%02x" "${1}"
    printf -v g "%02x" "${2}"
    printf -v b "%02x" "${3}"

    # Setting green to "0a" makes printf fail, at least on Pocket Reform
    if [[ "${g}" == "0a" ]]; then
        g="0b"
    fi
    # shellcheck disable=SC2059
    printf "xLRGB\x${b}\x${g}\x${r}" >"${hidraw_device}"
}

hidraw_leds_transition() {
    # Set leds transitioning from a start state to an end state.
    #
    # Args:
    #     r1(int): start red colour, range 0-255.
    #     g1(int): start green colour, range 0-255.
    #     b1(int): start blue colour, range 0-255.
    #     r2(int): end red colour, range 0-255.
    #     g2(int): end green colour, range 0-255.
    #     b2(int): end blue colour, range 0-255.
    #     steps(int): transition steps.
    #     wait(int): time to wait between two steps (seconds)
    #
    # Example:
    #     hidraw_leds_transition 0 0 64   0 0 128  10 10
    local wait
    local -i r1 g1 b1 r2 g2 b2 steps step
    r1="${1}" g1="${2}" b1="${3}" r2="${4}" g2="${5}" b2="${6}" steps="${7}" wait="${8}"

    for ((step = 1; step <= steps; step++)); do
        hidraw_leds_set \
            "$((r1 + (r2 - r1) * step / steps))" \
            "$((g1 + (g2 - g1) * step / steps))" \
            "$((b1 + (b2 - b1) * step / steps))"
        sleep "${wait}"
    done
}

battery_name_get() {
    # Return the battery for this machine
    #
    # Example:
    #     BAT0
    local machine
    if [[ -n "${__rl_battery_name:-}" ]]; then
        true # cached
    else
        machine_get && machine="${__return}"
        __rl_battery_name="${machine_battery_names["${machine}"]}"

        while ! [[ -d "/sys/class/power_supply/${__rl_battery_name}" ]]; do
            echo "Waiting for Reform battery to be up..."
            sleep 1
        done
    fi
    __return="${__rl_battery_name}"
}

battery_capacity_get() {
    # Return the remaining capacity in percentage (integer).
    # shellcheck disable=SC2154
    local battery_name

    battery_name_get && battery_name="${__return}"
    read -r __return <"/sys/class/power_supply/${battery_name}/capacity"
}

input_device_keyboard_get() {
    # Return the computer input device on /dev/input/by-id/...
    #
    # Example:
    #     /dev/input/by-id/usb-MNT_Pocket_Reform_Input_RP2040-event-kbd
    local machine
    local expression
    local path

    if [[ -n "${__rl_input_device_keyboard:-}" ]]; then
        true # cached
    else
        machine_get && machine="${__return}"
        expression="${machine_input_device_keyboards["${machine}"]}"
        while true; do
            for path in /dev/input/by-id/*; do
                [[ -e "${path}" ]] || break
                if [[ "${path}" =~ /dev/input/by-id/${expression}$ ]]; then
                    __rl_input_device_keyboard="${path}"
                    break 2
                fi
            done
            echo "Waiting for Reform keyboard input device device to be up..."
            sleep 1
        done
    fi
    __return="${__rl_input_device_keyboard}"
}
