#!/usr/bin/env bash

# Description:
#   Change the desktop wallpaper and generate a blurred version.
#
# Requirs:
# - zenity (for file selection dialog)
# - imagemagick (for image processing)
# - swww (wallpaper daemon)
# - notify-send (for notifications)
# - change-colortheme (from scripts/change-colortheme)
# - flock (usually part of util-linux)

set -euo pipefail

# Lock

exec {LOCK_FD}>/tmp/"$(basename "$0")".lock

flock -n "$LOCK_FD" || {
    echo "Another instance is running. Exiting."
    # notify-send -a "change-wallpaper" "Error" "Another instance is running. Exiting."
    exit 1
}

# Open a file selection dialog if no argument is provided

if [ -z "${1-}" ]; then
    image=$(zenity --file-selection --title="Open File" --file-filter="*.jpg *.jpeg *.png *.webp *.bmp *.jfif *.tiff *.avif *.heic *.heif")
else
    image="$1"
fi

[ -z "$image" ] && exit 1
[ ! -f "$image" ] && exit 1

# Obtain screen resolution

screen_width=${2-}
screen_height=${3-}

[ -z "$screen_width" ] && {
    if [ "$XDG_CURRENT_DESKTOP" = "Hyprland" ]; then
        screen_width=$(hyprctl -j monitors | jq '.[0].resolution.x')
    elif [ "$XDG_CURRENT_DESKTOP" = "niri" ]; then
        screen_width=$(niri msg focused-output | grep 'Current mode' | awk '{print $3}' | cut -d'x' -f1)
    fi
}

[ -z "$screen_height" ] && {
    if [ "$XDG_CURRENT_DESKTOP" = "Hyprland" ]; then
        screen_height=$(hyprctl -j monitors | jq '.[0].resolution.y')
    elif [ "$XDG_CURRENT_DESKTOP" = "niri" ]; then
        screen_height=$(niri msg focused-output | grep 'Current mode' | awk '{print $3}' | cut -d'x' -f2)
    fi
}

## Default to 2k
screen_width=${screen_width:-2560}
screen_height=${screen_height:-1440}

# $HOME/.config/wallpaper-chooser/config.json:
# ```json
# "sort": {
#    "type": "date",
#    "reverse": true
# }
# ```
# So in order to let the most recently used wallpapers appear first:
touch "$image" 2>/dev/null || true # ignore errors

# Copy image to local wallpaper directory

## Format of current and cached wallpaper
wallpaper_ext="png"
## Generate a random name for the current wallpaper
set +o pipefail # SIGPIPE is expected here
random_name=$(tr -dc 'a-zA-Z0-9' </dev/urandom | head -c 16)
set -o pipefail
## Directory to store current wallpaper
current_dir="$HOME/.local/share/wallpaper/current"
## Path to current wallpaper image
wallpaper_image="$current_dir/wallpaper-${random_name}.${wallpaper_ext}"

mkdir -p "$current_dir"

## Batch copy using a temporary file to avoid incomplete file being used
temp_img=$(mktemp --suffix=."$wallpaper_ext")
trap 'rm -f "$temp_img"' EXIT
magick "$image" -resize "${screen_width}x${screen_height}^" -gravity center -extent "${screen_width}x${screen_height}" "$temp_img"
cp "$temp_img" "$wallpaper_image"

## Generate hash for caching,
## based on content of the source image and resolution of the resized image
hash="$(md5sum "$image" | awk '{print $1}')-${screen_width}x${screen_height}"

# Clean up old wallpapers in the same directory of current wallpaper.
# Only keep the newly added one so the wallpaper-daemon can pick it up directly

find "$current_dir" -type f -name "wallpaper-*" ! -name "$(basename "$wallpaper_image")" -delete

# Generate blurred wallpaper

## Similarly, store blurred version of current wallpaper in separate directory
blur_dir="$HOME/.local/share/wallpaper/blurred"
## Directory to cache blurred wallpapers, so that we don't need to regenerate
## them every time when switching wallpapers. This makes it possible to have
## an auto-played slideshow with blurred wallpapers without noticeable delay.
blur_cache_dir="$HOME/.local/share/wallpaper/blurred-cache"
mkdir -p "$blur_dir" "$blur_cache_dir"
blurred_image="$blur_dir/blurred-${random_name}.${wallpaper_ext}"
blurred_cache_image="$blur_cache_dir/${hash}.${wallpaper_ext}"

## Time consuming task (magick -blur) in background
(
    # notify-send -a "change-wallpaper" "Generating Blurred Wallpaper" "This may take a few seconds..."

    function apply_blured {
        find "$blur_dir" -type f -name "blurred-*" ! -name "$(basename "$blurred_image")" -delete
        if [ "$XDG_CURRENT_DESKTOP" = "niri" ]; then
            swww img -n backdrop "$blurred_image" --transition-type fade --transition-duration 2 >/dev/null 2>/dev/null
        fi
        # notify-send -a "change-wallpaper" "Blurred Wallpaper Applied" "$blurred_image" -i "$blurred_image"
    }

    ### Check if cached blurred image exists
    if [ -f "$blurred_cache_image" ]; then
        # sleep 1 # Some ugly workaround
        if ! cp -f "$blurred_cache_image" "$blurred_image"; then
            echo "Could not copy cached blurred image"
            # exit 1 # Non-critical error
        else
            apply_blured
            exit 0
        fi
    fi

    sigma=$(magick identify -format "%w %h" "$wallpaper_image" | awk -v f=0.01 '{
        m=($1>$2)?$1:$2;
        s=m*f;
        if(s<2) s=2;
        if(s>200) s=200;
        printf "%.2f", s
    }')

    ### Batch processing using a temporary file to avoid incomplete file being used
    temp_blurred=$(mktemp --suffix=."$wallpaper_ext") || exit 1
    trap 'rm -f "${temp_blurred}"' EXIT
    magick "$wallpaper_image" -blur 0x"$sigma" "$temp_blurred" || {
        echo "Could not create blurred image"
        exit 1
    }

    mv -f "$temp_blurred" "$blurred_image" || {
        echo "Could not move blurred image to cache directory"
        exit 1
    }

    cp -f "$blurred_image" "$blurred_cache_image" || {
        echo "Could not cache blurred image"
        # exit 1 # Non-critical error
    }

    apply_blured
) &

# Apply wallpaper

skip_colortheme=0
if [ "${4-}" = "--skip-colortheme" ]; then
    skip_colortheme=1
fi

if [ "$XDG_CURRENT_DESKTOP" = "Hyprland" ]; then
    swww img -n background "$wallpaper_image" --transition-type fade --transition-duration 2 >/dev/null 2>/dev/null

    # notify-send -a "change-wallpaper" "Wallpaper Changed" "$image" -i "$wallpaper_image"

    if [ "$skip_colortheme" = 0 ]; then
        change-colortheme -i "$wallpaper_image" || exit 1
    fi
elif [ "$XDG_CURRENT_DESKTOP" = "niri" ]; then
    ### Handled in wallpaper-daemon
    # swww img -n background "$wallpaper_image" --transition-type fade --transition-duration 2 > /dev/null 2> /dev/null

    # notify-send -a "change-wallpaper" "Wallpaper Changed" "$image" -i "$wallpaper_image"

    if [ "$skip_colortheme" = 0 ]; then
        change-colortheme -i "$wallpaper_image" || exit 1
    fi
else
    echo "Unsupported desktop environment: $XDG_CURRENT_DESKTOP"
    exit 1
fi
