#!/bin/bash

if ! systemctl --user --quiet is-active xr-driver >/dev/null 2>&1; then
    if [ -z "$IGNORE_SYSTEMD_ERRORS" ]; then
        echo "xr-driver service is not active. Start it with:" >&2
        echo "  systemctl --user start xr-driver" >&2
        echo ""
        echo "If the issue persists, please create an issue on the wheaney/XRLinuxDriver repository."
        echo ""
        echo "To ignore this error, rerun with IGNORE_SYSTEMD_ERRORS=1"
        exit 1
    else
        echo "Warning: xr-driver service is not active, most actions will be ineffective until this is resolved. Continuing due to IGNORE_SYSTEMD_ERRORS..." >&2
    fi
fi

USER=${SUDO_USER:-$USER}
USER_HOME=$(getent passwd $USER | cut -d: -f6)
if [ -z "$XDG_CONFIG_HOME" ]; then
  XDG_CONFIG_HOME="$USER_HOME/.config"
fi
CONFIG_DIR="$XDG_CONFIG_HOME/xr_driver"
if [ ! -d "$CONFIG_DIR" ]; then
  mkdir -p $CONFIG_DIR
fi
if [ -z "$XDG_STATE_HOME" ]; then
  XDG_STATE_HOME="$USER_HOME/.local/state"
fi
LOG_FILE="$XDG_STATE_HOME/xr_driver/driver.log"

ensure_config_file() {
    local file="$1"
    if [ ! -f "$file" ]; then
        touch "$file"
    fi
}

write_config_value() {
    local file="$1"
    local key="$2"
    local value="$3"
    if grep -q "^$key=" "$file"; then
        sed -i "s/^$key=.*/$key=$value/" "$file"
    else
        echo "$key=$value" >> "$file"
    fi
}

read_config_value() {
    local file="$1"
    local key="$2"
    local type="$3"
    local value
    if [ "$type" == "string" ]; then
        value="Not set"
    else
        value="enabled"
    fi

    while read -r line; do
        if [[ $line == "$key="* ]]; then
            local raw_value=${line#*=}
            if [ "$type" == "string" ]; then
                value=$raw_value
            else
                if [ "$raw_value" == "true" ]; then
                    value="disabled"
                fi
            fi
            break
        fi
    done < "$file"

    echo "$value"
}

process_config() {
    local file="$1"
    local key="$2"
    local value="$3"
    local type="$4"
    local key2="$5"
    local value2="$6"
    local token_action="$7"
    local token_arg="$8"

    ensure_config_file "$file"

    if [[ -n $value ]]; then
        write_config_value "$file" "$key" "$value"
        if [[ -n $key2 ]]; then
            write_config_value "$file" "$key2" "$value2"
        fi
        return 0
    fi

    local read_value
    read_value=$(read_config_value "$file" "$key" "$type")

    if [[ -n $token_action ]]; then
        local hardware_id="$read_value"
        local url="https://eu.driver-backend.xronlinux.com/tokens/v1"
        local message=""

        if [[ "$token_action" == "request" || "$token_action" == "verify" ]] && ! command -v jq >/dev/null 2>&1; then
            echo "Error: jq utility not found. Install jq to use this feature." >&2
            exit 1
        fi

        if [ "$token_action" == "request" ]; then
            postbody=$(jq -n \
                                    --arg hardwareId "$hardware_id" \
                                    --arg email "$token_arg" \
                                    '{hardwareId: $hardwareId, email: $email}')
            message=$(curl -s -X POST -H "Content-Type: application/json" -d "$postbody" "$url" | jq -r '.message')
        elif [ "$token_action" == "verify" ]; then
            postbody=$(jq -n \
                                    --arg hardwareId "$hardware_id" \
                                    --arg token "$token_arg" \
                                    '{hardwareId: $hardwareId, token: $token}')
            message=$(curl -s -X PUT -H "Content-Type: application/json" -d "$postbody" "$url" | jq -r '.message')
        elif [ "$token_action" == "refresh" ]; then
            echo "refresh_device_license=true" > /dev/shm/xr_driver_control
            message="License refresh requested"
        elif [ "$token_action" == "get" ]; then
            message="$hardware_id"
        fi

        if [ -n "$message" ]; then
            echo "$message"
        fi
    else
        echo "$read_value"
    fi
}

require_arg() {
    local opt="$1"
    local arg="$2"
    if [ -z "$arg" ] || [ "$arg" == "--" ]; then
        echo "Error: $opt requires an argument." >&2
        exit 1
    fi
}

print_usage() {
    echo "Usage: $0 [options]
Options:
    -h, --help
    -l, --view-log
    -d, --disable
    -e, --enable
    -s, --status
    -j, --use-joystick
    -m, --use-mouse
    --invert-x, --no-invert-x
    --invert-y, --no-invert-y
    --invert-z, --no-invert-z
    --vr-lite-invert-x, --no-vr-lite-invert-x
    --vr-lite-invert-y, --no-vr-lite-invert-y
    --gamescope-reshade-wayland, --no-gamescope-reshade-wayland
    -ms, --mouse-sensitivity [sensitivity_value]
    --look-ahead-ms [milliseconds]
    -pa,--pitch-adjustment-degrees [degrees]
    --no-pitch-adjustment
    -dz, --deadzone-threshold-degrees [threshold_degrees]
    --multi-tap, --no-multi-tap
    -rc, --recenter
    -ds, --display-size [display_size]
    -dd, --display-distance [display_distance]
    -em, --external-mode
    -de, --disable-external
    -vd, --virtual-display
    -bd, --breezy-desktop
    --opentrack-app
    -sv, --sideview
    -svp, --sideview-position [top_left|top_right|bottom_left|bottom_right|middle_center|middle_left|top_center|middle_right|bottom_center]
    --smooth-follow, --no-smooth-follow
    -sft, --smooth-follow-threshold [threshold_value]
    --curved-display, --no-curved-display
    --smooth-follow-track-roll, --no-smooth-follow-track-roll
    --smooth-follow-track-pitch, --no-smooth-follow-track-pitch
    --smooth-follow-track-yaw, --no-smooth-follow-track-yaw
    -sbsms, --sbs-mode-stretched, --no-sbs-mode-stretched
    -sbs3d, --sbs-content-3d, --no-sbs-content-3d
    -nsh, --neck-saver-horizontal [multiplier]
    -nsv, --neck-saver-vertical [multiplier]
    --opentrack-app-ip [ip]
    --opentrack-app-port [port]
    --opentrack-listener, --no-opentrack-listener
    --opentrack-listen-ip [ip]
    --opentrack-listen-port [port]
    --metrics, --no-metrics
    --request-token [email]
    --verify-token [token]
    --refresh-license
    --get-hardware-id"
}

if [ $# -eq 0 ]; then
    print_usage
    exit 0
fi

if [ -n "$1" ]; then
    if ! command -v getopt >/dev/null 2>&1; then
        echo "Error: getopt utility not found. Install getopt to use this CLI." >&2
        exit 1
    fi

    normalized_args=()
    for arg in "$@"; do
        case "$arg" in
            -ms) normalized_args+=("--mouse-sensitivity") ;;
            -dz) normalized_args+=("--deadzone-threshold-degrees") ;;
            -ds) normalized_args+=("--display-size") ;;
            -dd) normalized_args+=("--display-distance") ;;
            -em) normalized_args+=("--external-mode") ;;
            -de) normalized_args+=("--disable-external") ;;
            -vd) normalized_args+=("--virtual-display") ;;
            -bd) normalized_args+=("--breezy-desktop") ;;
            -sv) normalized_args+=("--sideview") ;;
            -svp) normalized_args+=("--sideview-position") ;;
            -sft) normalized_args+=("--smooth-follow-threshold") ;;
            -sbsms) normalized_args+=("--sbs-mode-stretched") ;;
            -sbs3d) normalized_args+=("--sbs-content-3d") ;;
            -nsh) normalized_args+=("--neck-saver-horizontal") ;;
            -nsv) normalized_args+=("--neck-saver-vertical") ;;
            -rc) normalized_args+=("--recenter") ;;
            -pa) normalized_args+=("--pitch-adjustment-degrees") ;;
            *) normalized_args+=("$arg") ;;
        esac
    done

    PARSED=$(getopt -o hldesjm --long help,view-log,disable,enable,status,use-joystick,use-mouse,invert-x,no-invert-x,invert-y,no-invert-y,invert-z,no-invert-z,vr-lite-invert-x,no-vr-lite-invert-x,vr-lite-invert-y,no-vr-lite-invert-y,gamescope-reshade-wayland,no-gamescope-reshade-wayland,mouse-sensitivity:,look-ahead-ms:,pitch-adjustment-degrees:,no-pitch-adjustment,deadzone-threshold-degrees:,debug:,display-size:,display-distance:,external-mode,disable-external,virtual-display,breezy-desktop,opentrack-app,sideview,sideview-position:,smooth-follow,no-smooth-follow,smooth-follow-threshold:,curved-display,no-curved-display,smooth-follow-track-roll,no-smooth-follow-track-roll,smooth-follow-track-pitch,no-smooth-follow-track-pitch,smooth-follow-track-yaw,no-smooth-follow-track-yaw,sbs-mode-stretched,no-sbs-mode-stretched,sbs-content-3d,no-sbs-content-3d,multi-tap,no-multi-tap,recenter,neck-saver-horizontal:,neck-saver-vertical:,opentrack-app-ip:,opentrack-app-port:,opentrack-listener,no-opentrack-listener,opentrack-listen-ip:,opentrack-listen-port:,metrics,no-metrics,request-token:,verify-token:,refresh-license,get-hardware-id -- "${normalized_args[@]}")
    if [ $? -ne 0 ]; then
        exit 1
    fi

    eval set -- "$PARSED"

    default_config_file="$CONFIG_DIR/config.ini"
    state_config_file="/dev/shm/xr_driver_state"

    while true; do
        case "$1" in
            -h|--help)
                print_usage
                exit 0
                ;;
            -l|--view-log)
                less +F "$LOG_FILE"
                exit 0
                ;;
            -d|--disable)
                process_config "$default_config_file" "disabled" "true"
                shift
                ;;
            -e|--enable)
                process_config "$default_config_file" "disabled" "false"
                shift
                ;;
            -s|--status)
                process_config "$default_config_file" "disabled" "" ""
                shift
                ;;
            -j|--use-joystick)
                process_config "$default_config_file" "output_mode" "joystick" "string" "external_mode" "none"
                shift
                ;;
            -m|--use-mouse)
                process_config "$default_config_file" "output_mode" "mouse" "string" "external_mode" "none"
                shift
                ;;
            --invert-x)
                process_config "$default_config_file" "invert_x" "true"
                shift
                ;;
            --no-invert-x)
                process_config "$default_config_file" "invert_x" "false"
                shift
                ;;
            --invert-y)
                process_config "$default_config_file" "invert_y" "true"
                shift
                ;;
            --no-invert-y)
                process_config "$default_config_file" "invert_y" "false"
                shift
                ;;
            --invert-z)
                process_config "$default_config_file" "invert_z" "true"
                shift
                ;;
            --no-invert-z)
                process_config "$default_config_file" "invert_z" "false"
                shift
                ;;
            --vr-lite-invert-x)
                process_config "$default_config_file" "vr_lite_invert_x" "true"
                shift
                ;;
            --no-vr-lite-invert-x)
                process_config "$default_config_file" "vr_lite_invert_x" "false"
                shift
                ;;
            --vr-lite-invert-y)
                process_config "$default_config_file" "vr_lite_invert_y" "true"
                shift
                ;;
            --no-vr-lite-invert-y)
                process_config "$default_config_file" "vr_lite_invert_y" "false"
                shift
                ;;
            --gamescope-reshade-wayland)
                process_config "$default_config_file" "gamescope_reshade_wayland_disabled" "false"
                shift
                ;;
            --no-gamescope-reshade-wayland)
                process_config "$default_config_file" "gamescope_reshade_wayland_disabled" "true"
                shift
                ;;
            --mouse-sensitivity)
                require_arg "$1" "$2"
                process_config "$default_config_file" "mouse_sensitivity" "$2" "string"
                shift 2
                ;;
            --look-ahead-ms)
                require_arg "$1" "$2"
                process_config "$default_config_file" "look_ahead" "$2" "string"
                shift 2
                ;;
            -pa|--pitch-adjustment-degrees)
                require_arg "$1" "$2"
                process_config "$default_config_file" "pitch_adjustment_degrees" "$2" "string"
                process_config "$default_config_file" "use_pitch_adjustment_override" "true"
                shift 2
                ;;
            --no-pitch-adjustment)
                process_config "$default_config_file" "use_pitch_adjustment_override" "false"
                shift
                ;;
            --deadzone-threshold-degrees)
                require_arg "$1" "$2"
                process_config "$default_config_file" "dead_zone_threshold_deg" "$2" "string"
                shift 2
                ;;
            --debug)
                require_arg "$1" "$2"
                process_config "$default_config_file" "debug" "$2" "string"
                shift 2
                ;;
            --display-size)
                require_arg "$1" "$2"
                process_config "$default_config_file" "display_size" "$2" "string"
                shift 2
                ;;
            --display-distance)
                require_arg "$1" "$2"
                process_config "$default_config_file" "display_distance" "$2" "string"
                shift 2
                ;;
            --external-mode)
                process_config "$default_config_file" "external_mode" "" "string"
                shift
                ;;
            --disable-external)
                process_config "$default_config_file" "external_mode" "none" "string"
                shift
                ;;
            --virtual-display)
                process_config "$default_config_file" "output_mode" "external_only" "string" "external_mode" "virtual_display"
                shift
                ;;
            --breezy-desktop)
                process_config "$default_config_file" "output_mode" "external_only" "string" "external_mode" "breezy_desktop"
                shift
                ;;
            --opentrack-app)
                process_config "$default_config_file" "output_mode" "external_only" "string" "external_mode" "opentrack"
                shift
                ;;
            --sideview)
                process_config "$default_config_file" "output_mode" "external_only" "string" "external_mode" "sideview"
                shift
                ;;
            --sideview-position)
                require_arg "$1" "$2"
                process_config "$default_config_file" "sideview_position" "$2" "string"
                shift 2
                ;;
            --smooth-follow)
                process_config "$default_config_file" "sideview_smooth_follow_enabled" "true"
                shift
                ;;
            --no-smooth-follow)
                process_config "$default_config_file" "sideview_smooth_follow_enabled" "false"
                shift
                ;;
            --smooth-follow-threshold)
                require_arg "$1" "$2"
                process_config "$default_config_file" "sideview_follow_threshold" "$2" "string"
                shift 2
                ;;
            --curved-display)
                process_config "$default_config_file" "curved_display" "true"
                shift
                ;;
            --no-curved-display)
                process_config "$default_config_file" "curved_display" "false"
                shift
                ;;
            --smooth-follow-track-roll)
                process_config "$default_config_file" "smooth_follow_track_roll" "true"
                shift
                ;;
            --no-smooth-follow-track-roll)
                process_config "$default_config_file" "smooth_follow_track_roll" "false"
                shift
                ;;
            --smooth-follow-track-pitch)
                process_config "$default_config_file" "smooth_follow_track_pitch" "true"
                shift
                ;;
            --no-smooth-follow-track-pitch)
                process_config "$default_config_file" "smooth_follow_track_pitch" "false"
                shift
                ;;
            --smooth-follow-track-yaw)
                process_config "$default_config_file" "smooth_follow_track_yaw" "true"
                shift
                ;;
            --no-smooth-follow-track-yaw)
                process_config "$default_config_file" "smooth_follow_track_yaw" "false"
                shift
                ;;
            --sbs-mode-stretched)
                process_config "$default_config_file" "sbs_mode_stretched" "true" "string"
                shift
                ;;
            --no-sbs-mode-stretched)
                process_config "$default_config_file" "sbs_mode_stretched" "false" "string"
                shift
                ;;
            --sbs-content-3d)
                process_config "$default_config_file" "sbs_content" "true" "string"
                shift
                ;;
            --no-sbs-content-3d)
                process_config "$default_config_file" "sbs_content" "false" "string"
                shift
                ;;
            --neck-saver-horizontal)
                require_arg "$1" "$2"
                process_config "$default_config_file" "neck_saver_horizontal_multiplier" "$2" "string"
                shift 2
                ;;
            --neck-saver-vertical)
                require_arg "$1" "$2"
                process_config "$default_config_file" "neck_saver_vertical_multiplier" "$2" "string"
                shift 2
                ;;
            --opentrack-app-ip)
                require_arg "$1" "$2"
                process_config "$default_config_file" "opentrack_app_ip" "$2" "string"
                shift 2
                ;;
            --opentrack-app-port)
                require_arg "$1" "$2"
                process_config "$default_config_file" "opentrack_app_port" "$2" "string"
                shift 2
                ;;
            --opentrack-listener)
                process_config "$default_config_file" "opentrack_listener_enabled" "true"
                shift
                ;;
            --no-opentrack-listener)
                process_config "$default_config_file" "opentrack_listener_enabled" "false"
                shift
                ;;
            --opentrack-listen-ip)
                require_arg "$1" "$2"
                process_config "$default_config_file" "opentrack_listen_ip" "$2" "string"
                shift 2
                ;;
            --opentrack-listen-port)
                require_arg "$1" "$2"
                process_config "$default_config_file" "opentrack_listen_port" "$2" "string"
                shift 2
                ;;
            --multi-tap)
                process_config "$default_config_file" "multi_tap_enabled" "true"
                shift
                ;;
            --no-multi-tap)
                process_config "$default_config_file" "multi_tap_enabled" "false"
                shift
                ;;
            -rc|--recenter)
                echo "recenter_screen=true" > /dev/shm/xr_driver_control
                shift
                ;;
            --metrics)
                process_config "$default_config_file" "metrics_disabled" "false"
                shift
                ;;
            --no-metrics)
                process_config "$default_config_file" "metrics_disabled" "true"
                shift
                ;;
            --request-token)
                require_arg "$1" "$2"
                process_config "$state_config_file" "hardware_id" "" "string" "" "" "request" "$2"
                shift 2
                ;;
            --verify-token)
                require_arg "$1" "$2"
                process_config "$state_config_file" "hardware_id" "" "string" "" "" "verify" "$2"
                shift 2
                ;;
            --refresh-license)
                process_config "$state_config_file" "hardware_id" "" "string" "" "" "refresh"
                shift
                ;;
            --get-hardware-id)
                process_config "$state_config_file" "hardware_id" "" "string" "" "" "get"
                shift
                ;;
            --)
                shift
                break
                ;;
            *)
                break
                ;;
        esac
    done
fi
