#!/usr/bin/env bash
# Per-monitor wallpaper picker for hyprpaper.
# IPC docs: https://wiki.hypr.land/Hypr-Ecosystem/hyprpaper/

set -u

VERBOSE=0
DIR=""
STATE_FILE="${HOME}/.config/wallpaper.conf"
FIT_MODE="cover"

while [[ $# -gt 0 ]]; do
    case "$1" in
        -v|--verbose) VERBOSE=1 ;;
        --fit) shift; FIT_MODE="$1" ;;
        --state) shift; STATE_FILE="$1" ;;
        *) DIR="$1" ;;
    esac
    shift
done

DIR="${DIR:-$HOME/Pictures}"
LOG=""
(( VERBOSE )) && LOG="/tmp/wallpaper-picker-$$.log"
vlog() { (( VERBOSE )) && printf '%s\n' "$*" >> "$LOG"; }

IMAGES=()
while IFS= read -r -d '' f; do
    IMAGES+=("$f")
done < <(find "$DIR" -maxdepth 1 -type f \
    \( -iname '*.jpg' -o -iname '*.jpeg' -o -iname '*.png' \
       -o -iname '*.webp' -o -iname '*.bmp' -o -iname '*.gif' \
       -o -iname '*.avif' -o -iname '*.tiff' \) \
    -print0 | sort -z)

[[ ${#IMAGES[@]} -eq 0 ]] && { echo "No images found in $DIR"; exit 1; }

MONITORS=()
while IFS= read -r m; do
    [[ -n "$m" ]] && MONITORS+=("$m")
done < <(hyprctl monitors 2>/dev/null | awk '/^Monitor /{print $2}')
[[ ${#MONITORS[@]} -eq 0 ]] && { echo "No monitors detected — is Hyprland running?"; exit 1; }

mouse_monitor() {
    command -v jq >/dev/null 2>&1 || return 1
    local pos cx cy
    pos=$(hyprctl cursorpos -j 2>/dev/null) || return 1
    cx=$(printf '%s' "$pos" | jq -r '.x // empty')
    cy=$(printf '%s' "$pos" | jq -r '.y // empty')
    [[ -z "$cx" || -z "$cy" ]] && return 1
    hyprctl monitors -j 2>/dev/null | jq -r --argjson cx "$cx" --argjson cy "$cy" '
        .[] |
        ((.transform % 2) == 1) as $rot |
        (if $rot then .height else .width end) / .scale as $lw |
        (if $rot then .width else .height end) / .scale as $lh |
        select(.x <= $cx and $cx < .x + $lw and .y <= $cy and $cy < .y + $lh) |
        .name
    ' 2>/dev/null | head -n1
}

declare -A CURRENT
declare -A LOADED
while IFS= read -r line; do
    mon="${line%%: *}"
    path="${line#*: }"
    [[ -z "$mon" || -z "$path" || "$mon" == "$line" ]] && continue
    CURRENT[$mon]="$path"
    LOADED[$path]=1
done < <(hyprctl hyprpaper listactive 2>/dev/null)

declare -A SELECTED
default_mon=$(mouse_monitor || true)
if [[ -z "$default_mon" ]]; then
    default_mon=$(hyprctl monitors -j 2>/dev/null | jq -r '.[] | select(.focused) | .name' 2>/dev/null | head -n1)
fi
[[ -z "$default_mon" ]] && default_mon="${MONITORS[0]}"
SELECTED[$default_mon]=1

INDEX=0

icat() { kitty +kitten icat --stdin=no --silent --transfer-mode=memory "$@" 2>/dev/null; }
clear_images() { icat --clear; }

selected_count() { printf '%d' "${#SELECTED[@]}"; }

target_label() {
    local mon out=""
    if (( ${#SELECTED[@]} == 0 )); then
        printf 'none'; return
    fi
    if (( ${#SELECTED[@]} == ${#MONITORS[@]} )); then
        printf 'all'; return
    fi
    for mon in "${MONITORS[@]}"; do
        [[ -n "${SELECTED[$mon]:-}" ]] && out+="$mon, "
    done
    printf '%s' "${out%, }"
}

draw_targets() {
    local i mon out=""
    for (( i=0; i<${#MONITORS[@]}; i++ )); do
        mon="${MONITORS[$i]}"
        if [[ -n "${SELECTED[$mon]:-}" ]]; then
            out+=$'\033[7m'" $((i+1)) ${mon} "$'\033[0m'"  "
        else
            out+=" $((i+1)) ${mon}    "
        fi
    done
    if (( ${#SELECTED[@]} == ${#MONITORS[@]} )); then
        out+=$'\033[7m'" a all "$'\033[0m'
    else
        out+=" a all "
    fi
    printf '%s' "$out"
}

draw_status() {
    local mon name marker
    for mon in "${MONITORS[@]}"; do
        marker=" "
        [[ -n "${SELECTED[$mon]:-}" ]] && marker="•"
        name="${CURRENT[$mon]:-}"
        [[ -n "$name" ]] && name="$(basename "$name")" || name="—"
        printf '  %s %-12s %s\n' "$marker" "$mon:" "$name"
    done
}

draw() {
    local cols rows
    cols=$(tput cols); rows=$(tput lines)

    local gap=2
    local side_w=$(( cols * 18 / 100 ))
    local center_w=$(( cols - 2 * (side_w + gap) ))
    local center_x=$(( side_w + gap ))
    local next_x=$(( center_x + center_w + gap ))
    local header_h=2
    local footer_h=$(( ${#MONITORS[@]} + 3 ))
    local img_h=$(( rows - header_h - footer_h ))
    (( img_h < 5 )) && img_h=5
    local n=${#IMAGES[@]}

    local prev=$(( (INDEX - 1 + n) % n ))
    local next=$(( (INDEX + 1) % n ))

    printf '\033[2J\033[H'
    clear_images

    tput cup 0 0
    printf 'target: %s' "$(draw_targets)"

    icat --place "${side_w}x${img_h}@0x${header_h}"             "${IMAGES[$prev]}"
    icat --place "${center_w}x${img_h}@${center_x}x${header_h}" "${IMAGES[$INDEX]}"
    icat --place "${side_w}x${img_h}@${next_x}x${header_h}"     "${IMAGES[$next]}"

    local row=$(( header_h + img_h ))
    tput cup "$row" 0
    printf '  [%d/%d] %s\n' $(( INDEX + 1 )) "$n" "$(basename "${IMAGES[$INDEX]}")"
    draw_status
    local hints='  h/←: prev  l/→: next  1-9: toggle monitor  a: toggle all  Enter/Space: apply  q: quit'
    (( VERBOSE )) && hints+="  [log: $LOG]"
    printf '%s' "$hints"
}

write_state() {
    local mon path tmp
    tmp=$(mktemp "${STATE_FILE}.XXXXXX") || return 1
    {
        printf '# generated by wallpaper-picker — do not edit by hand\n'
        for mon in "${MONITORS[@]}"; do
            path="${CURRENT[$mon]:-}"
            [[ -z "$path" ]] && continue
            printf 'wallpaper {\n    monitor = %s\n    path = %s\n    fit_mode = %s\n}\n' \
                "$mon" "$path" "$FIT_MODE"
        done
    } > "$tmp" && mv -f "$tmp" "$STATE_FILE"
    vlog "wrote $STATE_FILE"
}

apply() {
    local path="$1"
    local out rc

    (( ${#SELECTED[@]} == 0 )) && return 2

    if [[ -z "${LOADED[$path]:-}" ]]; then
        out=$(hyprctl hyprpaper preload "$path" 2>&1); rc=$?
        vlog "preload exit=$rc out=$out"
        LOADED[$path]=1
    fi

    local mon ok=0
    for mon in "${!SELECTED[@]}"; do
        out=$(hyprctl hyprpaper wallpaper "$mon, $path" 2>&1); rc=$?
        vlog "wallpaper $mon exit=$rc out=$out"
        if (( rc == 0 )); then
            CURRENT[$mon]="$path"
            ok=1
        fi
    done

    (( ok == 0 )) && return 1

    declare -A in_use
    for mon in "${MONITORS[@]}"; do
        [[ -n "${CURRENT[$mon]:-}" ]] && in_use[${CURRENT[$mon]}]=1
    done
    local p
    for p in "${!LOADED[@]}"; do
        if [[ -z "${in_use[$p]:-}" ]]; then
            out=$(hyprctl hyprpaper unload "$p" 2>&1); rc=$?
            vlog "unload $p exit=$rc out=$out"
            unset "LOADED[$p]"
        fi
    done

    write_state
    return 0
}

KEY=""
read_key() {
    KEY=""
    IFS= read -rsn1 KEY
    if [[ $KEY == $'\x1b' ]]; then
        local rest=""
        IFS= read -rsn2 -t 0.1 rest || true
        KEY+="$rest"
    fi
    if (( VERBOSE )); then
        local hex
        hex=$(printf '%s' "$KEY" | od -An -tx1 | tr -d ' \n')
        vlog "key: hex=$hex len=${#KEY}"
    fi
}

old_stty=$(stty -g)
trap 'stty "$old_stty"; clear_images; tput cnorm; printf "\033[2J\033[H"' EXIT
stty -echo -icanon -icrnl min 1 time 0
tput civis

draw

while true; do
    read_key
    case "$KEY" in
        h|$'\x1b[D')
            INDEX=$(( (INDEX - 1 + ${#IMAGES[@]}) % ${#IMAGES[@]} ))
            draw ;;
        l|$'\x1b[C')
            INDEX=$(( (INDEX + 1) % ${#IMAGES[@]} ))
            draw ;;
        a|A)
            if (( ${#SELECTED[@]} == ${#MONITORS[@]} )); then
                SELECTED=()
            else
                SELECTED=()
                for mon in "${MONITORS[@]}"; do SELECTED[$mon]=1; done
            fi
            draw ;;
        [1-9])
            n=$KEY
            if (( n >= 1 && n <= ${#MONITORS[@]} )); then
                mon="${MONITORS[$((n-1))]}"
                if [[ -n "${SELECTED[$mon]:-}" ]]; then
                    unset "SELECTED[$mon]"
                else
                    SELECTED[$mon]=1
                fi
                draw
            fi ;;
        $'\r'|$'\n'|$'\x1bOM'|' ')
            label=$(target_label)
            apply "${IMAGES[$INDEX]}"; rc=$?
            tput cup $(( $(tput lines) - 1 )) 0
            case $rc in
                0) printf '\033[2K  Set on %s: %s' "$label" "$(basename "${IMAGES[$INDEX]}")" ;;
                2) printf '\033[2K  No monitors selected — press 1-%d or a' "${#MONITORS[@]}" ;;
                *) printf '\033[2K  Failed — is hyprpaper running?'
                   (( VERBOSE )) && printf ' [see %s]' "$LOG" ;;
            esac ;;
        q|Q) break ;;
    esac
done
