#!/bin/bash
set -euo pipefail

LED_PATH="/sys/kernel/debug/ec/ec0/io"
LED_ON="\\x8a"
LED_OFF="\\x0a"
LED_BLINK="\\xca"
WRITE_SUPPORT_PATH="/sys/module/ec_sys/parameters/write_support"
STATE_DIR="/var/lib/thinkpad-red-led"
STATE_FILE="${STATE_DIR}/state"

require_root() {
    if [[ "${EUID}" -ne 0 ]]; then
        echo "This helper must be run as root." >&2
        exit 1
    fi
}

ensure_state_dir() {
    install -d -m 0755 "$STATE_DIR"
}

save_state() {
    local state="$1"
    ensure_state_dir
    printf '%s\n' "$state" > "$STATE_FILE"
    chmod 0644 "$STATE_FILE"
}

read_state() {
    if [[ -r "$STATE_FILE" ]]; then
        tr -d '\n' < "$STATE_FILE"
    fi
}

is_write_support_enabled() {
    local value=""
    if [[ -r "$WRITE_SUPPORT_PATH" ]]; then
        value="$(tr -d '\n' < "$WRITE_SUPPORT_PATH")"
    fi
    case "$value" in
        1|Y|y|true|on) return 0 ;;
    esac
    return 1
}

enable_write_support() {
    if [[ ! -w "$WRITE_SUPPORT_PATH" ]]; then
        return 1
    fi

    if is_write_support_enabled; then
        return 0
    fi

    if printf '1' > "$WRITE_SUPPORT_PATH" 2>/dev/null; then
        if is_write_support_enabled; then
            return 0
        fi
    fi

    if printf 'Y' > "$WRITE_SUPPORT_PATH" 2>/dev/null; then
        if is_write_support_enabled; then
            return 0
        fi
    fi

    return 1
}

ensure_led_path() {
    if [[ ! -w "$LED_PATH" ]]; then
        echo "LED path not writable: $LED_PATH" >&2
        echo "write_support may be disabled for ec_sys." >&2
        exit 1
    fi
}

load_ec_sys() {
    local err=""

    if [[ -d /sys/module/ec_sys ]]; then
        if enable_write_support; then
            ensure_led_path
            return
        fi
        modprobe -r ec_sys >/dev/null 2>&1 || true
    fi

    if err=$(modprobe ec_sys write_support=1 2>&1); then
        ensure_led_path
        return
    fi

    if err=$(modprobe ec_sys write_support=Y 2>&1); then
        ensure_led_path
        return
    fi

    if err=$(modprobe ec_sys 2>&1); then
        if enable_write_support; then
            ensure_led_path
            return
        fi
    fi

    echo "Failed to enable ec_sys write_support." >&2
    if [[ -n "$err" ]]; then
        echo "Last error from modprobe: $err" >&2
    fi
    echo "If this kernel only allows it at boot, use: ec_sys.write_support=1" >&2
    exit 1
}

write_led() {
    printf '%b' "$1" | dd of="$LED_PATH" bs=1 seek=12 count=1 conv=notrunc 2>/dev/null
}

led_on() { write_led "$LED_ON"; }
led_off() { write_led "$LED_OFF"; }
led_blink() { write_led "$LED_BLINK"; }

restore_state() {
    local state=""
    if [[ ! -r "$STATE_FILE" ]]; then
        echo "No saved LED state at $STATE_FILE, skipping restore." >&2
        return 0
    fi

    state="$(read_state)"
    case "$state" in
        on)
            load_ec_sys
            led_on
            ;;
        off)
            load_ec_sys
            led_off
            ;;
        blink)
            load_ec_sys
            led_blink
            ;;
        *)
            echo "Invalid LED state in $STATE_FILE: $state" >&2
            return 0
            ;;
    esac
}

dit() {
    led_on
    sleep 0.3
    led_off
    sleep 0.15
}

dah() {
    led_on
    sleep 0.8
    led_off
    sleep 0.15
}

morse_char() {
    case "$1" in
        "0") dah; dah; dah; dah; dah;;
        "1") dit; dah; dah; dah; dah;;
        "2") dit; dit; dah; dah; dah;;
        "3") dit; dit; dit; dah; dah;;
        "4") dit; dit; dit; dit; dah;;
        "5") dit; dit; dit; dit; dit;;
        "6") dah; dit; dit; dit; dit;;
        "7") dah; dah; dit; dit; dit;;
        "8") dah; dah; dah; dit; dit;;
        "9") dah; dah; dah; dah; dit;;
        "a") dit; dah;;
        "b") dah; dit; dit; dit;;
        "c") dah; dit; dah; dit;;
        "d") dah; dit; dit;;
        "e") dit;;
        "f") dit; dit; dah; dit;;
        "g") dah; dah; dit;;
        "h") dit; dit; dit; dit;;
        "i") dit; dit;;
        "j") dit; dah; dah; dah;;
        "k") dah; dit; dah;;
        "l") dit; dah; dit; dit;;
        "m") dah; dah;;
        "n") dah; dit;;
        "o") dah; dah; dah;;
        "p") dit; dah; dah; dit;;
        "q") dah; dah; dit; dah;;
        "r") dit; dah; dit;;
        "s") dit; dit; dit;;
        "t") dah;;
        "u") dit; dit; dah;;
        "v") dit; dit; dit; dah;;
        "w") dit; dah; dah;;
        "x") dah; dit; dit; dah;;
        "y") dah; dit; dah; dah;;
        "z") dah; dah; dit; dit;;
        " ") sleep 0.6;;
        *) echo "Unsupported character: $1" >&2; exit 2;;
    esac
    sleep 0.2
}

morse_text() {
    local text="$1"
    text="${text,,}"

    if [[ -z "$text" ]]; then
        echo "Missing text for morse." >&2
        exit 2
    fi

    if [[ ! "$text" =~ ^[0-9a-z\ ]*$ ]]; then
        echo "Only 0-9, a-z, and space are supported." >&2
        exit 2
    fi

    led_off
    for ((i = 0; i < ${#text}; i++)); do
        morse_char "${text:i:1}"
    done

    sleep 1
    led_on
    modprobe -r ec_sys || true
}

usage() {
    echo "Usage: $0 on|off|blink|morse <text>|restore" >&2
    exit 2
}

main() {
    require_root

    if [[ $# -lt 1 ]]; then
        usage
    fi

    case "$1" in
        on)
            load_ec_sys
            led_on
            save_state "on"
            ;;
        off)
            load_ec_sys
            led_off
            save_state "off"
            ;;
        blink)
            load_ec_sys
            led_blink
            save_state "blink"
            ;;
        morse)
            if [[ $# -lt 2 ]]; then
                usage
            fi
            load_ec_sys
            morse_text "$2"
            save_state "on"
            ;;
        restore)
            restore_state
            ;;
        *)
            usage
            ;;
    esac
}

main "$@"
