#!/usr/bin/env bash

# Author : Copyright (c) 2017-2023 Pavan Jadhaw, and others (https://github.com/betterlockscreen/betterlockscreen/graphs/contributors)
# Project Repository : https://github.com/betterlockscreen/betterlockscreen

cmd_exists () {
    command -v "$1" >/dev/null
}

init_config () {
    # default options
    display_on=0
    span_image=false
    lock_timeout=300
    fx_list=(dim blur dimblur pixel dimpixel color)
    dim_level=40
    blur_level=1
    pixel_scale=10,1000
    solid_color=333333
    description=""
    quiet=false
    i3lockcolor_bin="i3lock-color"
    suspend_command="systemctl suspend"
    convert_command="magick"
    composite_command="magick composite"

    if ! cmd_exists "$i3lockcolor_bin" && cmd_exists "i3lock"; then
        i3lockcolor_bin="i3lock"
    fi

    if ! cmd_exists "magick"; then
        convert_command="convert"
        composite_command="composite"
    fi

    # default theme
    loginbox=00000066
    loginshadow=00000000
    locktext="Type password to unlock..."
    font="sans-serif"
    ringcolor=ffffffff
    insidecolor=00000000
    separatorcolor=00000000
    ringvercolor=ffffffff
    insidevercolor=00000000
    ringwrongcolor=ffffffff
    insidewrongcolor=d23c3dff
    timecolor=ffffffff
    time_format="%H:%M:%S"
    greetercolor=ffffffff
    layoutcolor=ffffffff
    keyhlcolor=d23c3dff
    bshlcolor=d23c3dff
    veriftext="Verifying..."
    verifcolor=ffffffff
    wrongtext="Failure!"
    wrongcolor=d23c3dff
    modifcolor=d23c3dff
    bgcolor=000000ff
    wallpaper_cmd="feh --bg-fill"

    # read user config
    USER_CONF_DIR="${XDG_CONFIG_HOME:-$HOME/.config}"
    USER_CONF="$USER_CONF_DIR/betterlockscreenrc"
    SYS_CONF="/etc/betterlockscreenrc"
    XDG_USER_CONF_DIR="$USER_CONF_DIR/betterlockscreen"
    XDG_USER_CONF="$XDG_USER_CONF_DIR/betterlockscreenrc"

    if [ -e "$SYS_CONF" ]; then
        # shellcheck source=/dev/null
        source "$SYS_CONF"
    fi

    if [ -e "$USER_CONF" ]; then
        echof error "Please, migrate your config $USER_CONF to $XDG_USER_CONF. Old location will soon be deprecated."
        echof info "mkdir -p ~/.config/betterlockscreen/ && mv $USER_CONF $XDG_USER_CONF"

        # shellcheck source=/dev/null
        source "$USER_CONF"
    fi

    if [ -e "$XDG_USER_CONF" ]; then
        # shellcheck source=/dev/null
        source "$XDG_USER_CONF"
    fi

    if ! cmd_exists "$i3lockcolor_bin"; then
        echof error "Unable to find i3lock-color binary under detected/configured name: '$i3lockcolor_bin'!"
        exit
    fi

    # Please make sure to adjust this before release!
    VERSION="4.3.0"

    # paths
    CACHE_DIR="${XDG_CACHE_HOME:-$HOME/.cache}/betterlockscreen"
    CUR_DIR="$CACHE_DIR/current"

    # wallpaper
    CUR_W_RESIZE="$CUR_DIR/wall_resize.png"
    CUR_W_DIM="$CUR_DIR/wall_dim.png"
    CUR_W_BLUR="$CUR_DIR/wall_blur.png"
    CUR_W_DIMBLUR="$CUR_DIR/wall_dimblur.png"
    CUR_W_PIXEL="$CUR_DIR/wall_pixel.png"
    CUR_W_DIMPIXEL="$CUR_DIR/wall_dimpixel.png"
    CUR_W_COLOR="$CUR_DIR/wall_color.png"

    # lockscreen
    CUR_L_RESIZE="$CUR_DIR/lock_resize.png"
    CUR_L_DIM="$CUR_DIR/lock_dim.png"
    CUR_L_BLUR="$CUR_DIR/lock_blur.png"
    CUR_L_DIMBLUR="$CUR_DIR/lock_dimblur.png"
    CUR_L_PIXEL="$CUR_DIR/lock_pixel.png"
    CUR_L_DIMPIXEL="$CUR_DIR/lock_dimpixel.png"
    CUR_L_COLOR="$CUR_DIR/lock_color.png"

    # Original DPMS timeout
    DEFAULT_TIMEOUT=$(cut -d ' ' -f4 <<< "$(xset q | sed -n '25p')")
    # Original DPMS status
    DEFAULT_DPMS=$(xset q | awk '/^[[:blank:]]*DPMS is/ {print $(NF)}')

    # Dunst
    DUNST_INSTALLED=false && [[ -e "$(command -v dunstctl)" ]] && DUNST_INSTALLED=true
    DUNST_IS_RUNNING=false && [[ "$DUNST_INSTALLED" == "true" ]] && [[ "$(pgrep -c dunst)" -gt 0 ]] && DUNST_IS_RUNNING=true
    DUNST_IS_PAUSED=false && [[ "$DUNST_IS_RUNNING" == "true" ]] && DUNST_IS_PAUSED=$(dunstctl is-paused)

    # Feh
    FEH_INSTALLED=false && [[ -e "$(command -v feh)" ]] && FEH_INSTALLED=true
}

# called before screen is locked
prelock() {
    # set dpms timeout
    if [ "$DEFAULT_DPMS" == "Enabled" ]; then
        xset dpms "$lock_timeout"
    fi

    # If dusnt is already paused don't pause it again
    if [[ "$DUNST_IS_RUNNING" == "true" && "$DUNST_IS_PAUSED" == "false" ]]; then
        dunstctl set-paused true
    fi

    if [ -e "$XDG_USER_CONF_DIR/custom-pre.sh" ]; then
        # shellcheck source=/dev/null
        source "$XDG_USER_CONF_DIR/custom-pre.sh"
    fi
}

# lock screen with specified image
lock() {
    local image="$1"
    local fontlg=32
    local fontmd=16
    local fontsm=12

    if [ -f "$image" ]; then
        echof act "Locking screen..."
    else
        echof act "Locking screen... (FAILSAFE MODE)"
    fi

    $i3lockcolor_bin \
        ${image:+-i "$image"} \
        --color "$bgcolor" \
        ${display_on:+-S "$display_on"} \
        --ind-pos="x+310:y+h-80" \
        --radius=25 \
        --ring-width=5 \
        --inside-color="$insidecolor" \
        --ring-color="$ringcolor" \
        --separator-color=$separatorcolor \
        --insidever-color="$insidevercolor" \
        --insidewrong-color="$insidewrongcolor" \
        --ringver-color="$ringvercolor" \
        --ringwrong-color="$ringwrongcolor" \
        --line-uses-inside \
        --keyhl-color="$keyhlcolor" \
        --bshl-color="$bshlcolor" \
        --clock --force-clock \
        --time-pos="ix-265:iy-10" \
        --time-align 1 \
        --time-str "$time_format" \
        --time-color="$timecolor" \
        --time-font="$font" \
        --time-size="$fontlg" \
        --date-str "" \
        --greeter-pos="ix-265:iy+12" \
        --greeter-align 1 \
        --greeter-text "$locktext" \
        --greeter-color="$greetercolor" \
        --greeter-font="$font" \
        --greeter-size="$fontmd" \
        --layout-pos="ix-265:iy+32" \
        --layout-align 1 \
        --layout-color="$layoutcolor" \
        --layout-font="$font" \
        --layout-size="$fontsm" \
        --verif-pos="ix+35:iy-34" \
        --verif-align 2 \
        --verif-text="$veriftext" \
        --verif-color="$verifcolor" \
        --verif-font="$font" \
        --verif-size="$fontsm" \
        --wrong-pos="ix+24:iy-34" \
        --wrong-align 2 \
        --wrong-text="$wrongtext" \
        --wrong-color="$wrongcolor" \
        --wrong-font="$font" \
        --wrong-size="$fontsm" \
        --modif-pos="ix+45:iy+43" \
        --modif-align 2 \
        --modif-size="$fontsm" \
        --modif-color="$modifcolor" \
        --noinput-text="" \
        --pass-media-keys \
        --pass-screen-keys \
        --pass-volume-keys \
        --pass-power-keys \
        "${lockargs[@]}"
}

# called after screen is unlocked
postlock() {
    # restore default dpms timeout
    if [ "$DEFAULT_DPMS" == "Enabled" ]; then
        xset dpms "$DEFAULT_TIMEOUT"
    fi

    # If dunst already paused before locking don't unpause dunst
    if [[ "$DUNST_IS_RUNNING" == "true" && "$DUNST_IS_PAUSED" == "false" ]]; then
        dunstctl set-paused false
    fi

    if [ -e "$XDG_USER_CONF_DIR/custom-post.sh" ]; then
        # shellcheck source=/dev/null
        source "$XDG_USER_CONF_DIR/custom-post.sh"
    fi
}

# select effect and lock screen
lockinit() {
    if pgrep -u "$USER" "$i3lockcolor_bin"; then
        echof error "i3lock already running"
        exit 1
    fi

    echof act "Running prelock..."
    prelock

    if [[ $runsuspend ]]; then
        lockselect "$@" &
        $suspend_command
        wait $!
    else
        lockselect "$@"
    fi

    echof act "Running postlock..."
    postlock
}

lockselect() {
    case "$1" in
        dim) lock "$CUR_L_DIM" ;;
        blur) lock "$CUR_L_BLUR" ;;
        dimblur) lock "$CUR_L_DIMBLUR" ;;
        pixel) lock "$CUR_L_PIXEL" ;;
        dimpixel) lock "$CUR_L_DIMPIXEL" ;;
        color) lock "$CUR_L_COLOR" ;;
        *) lock "$CUR_L_RESIZE" ;;
    esac
}

# calculate adjustments for hidpi displays
logical_px() {
    # $1: number of pixels to convert
    # $2: 1 for width. 2 for height
    local pixels="$1"
    local direction="$2"
    local dpi

    # use DPI set by user in .Xresources
    dpi=$(xrdb -q | grep -oP '^\s*Xft.dpi:\s*\K\d+' | bc)

    # or get dpi value from xdpyinfo
    if [ -z "$dpi" ]; then
        dpi=$(xdpyinfo | sed -En "s/\s*resolution:\s*([0-9]*)x([0-9]*)\s.*/\\$direction/p" | head -n1)
    fi

    # adjust scaling
    if [ -n "$dpi" ]; then
        local scale
        scale=$(echo "scale=3; $dpi / 96.0" | bc)
        echo "$scale * $pixels / 1" | bc
    else
        # return the default value if no DPI is set
        echo "$pixels"
    fi
}

# get total resolution, sets $TOTAL_SIZE
get_total_size () {
    TOTAL_SIZE=$(xdpyinfo | grep -w "dimensions" | sed -r 's/^[^0-9]*([0-9]+x[0-9]+).*$/\1/')
}

# get list of displays, sets $DISPLAY_LIST
get_display_list () {
    local count=0
    mapfile -t displays < <(xrandr --listactivemonitors)
    for display in "${displays[@]:1}"; do
        ((count++))
        display="$(echo "$display" | sed -r 's/\/[0-9]*//g')"
        IFS=' ' read -r -a info  <<< "$display"
        DISPLAY_LIST+=("$count ${info[3]} ${info[2]}")
    done
}

# populate $WALL_LIST depending on number of displays and images passed
get_wall_list() {
    local paths=("$@")
    declare -ga WALL_LIST

    # multiple images and spanning conflict, bail out
    if [ "${#paths[@]}" -gt 1 ] && [ "$span_image" = true ]; then
        echof err "Can't use --span with multiple images!"
        exit 1
    fi

    # if spanning return 1 image
    if [ "$span_image" = true ]; then
        get_image "${paths[0]}"

    # if # paths is 1
    elif [ "${#paths[@]}" -eq 1 ]; then
        for ((i=0; i<${#DISPLAY_LIST[@]}; i++)); do
            # add same image to $WALL_LIST for each display
            get_image "${paths[0]}"
        done

    # if # of paths equals # of displays
    elif [ ${#paths[@]} -eq "${#DISPLAY_LIST[@]}" ]; then
        for ((i=0; i<${#DISPLAY_LIST[@]}; i++)); do
            # add each image to $WALL_LIST
            get_image "${paths[$i]}"
        done

    # if # of paths differ from # of display, bail out
    else
        echof err "${#paths[@]} images provided for ${#DISPLAY_LIST[@]} displays!"
        exit 1
    fi
}

# get image path, append to $WALL_LIST
get_image() {
    local path="$1"

    # we have a file
    if [ -f "$path" ]; then
        WALL_LIST+=("$path")
        return
    # we have a directory
    elif [ -d "$path" ]; then
        dir=("$path"/*)
        rdir="${dir[RANDOM % ${#dir[@]}]}"
        get_image "$rdir" # <-- calls itself
    # not file or directory, bail out
    else
        echof err "invalid path: $path"
        exit 1
    fi

}

# scale base image and generate effects
resize_and_render () {
    local base="$1"
    local path="$2"
    local resolution="$3"

    # resource paths
    RES_RESIZE="$path/resize.png"
    RES_DIM="$path/dim.png"
    RES_BLUR="$path/blur.png"
    RES_DIMBLUR="$path/dimblur.png"
    RES_PIXEL="$path/pixel.png"
    RES_DIMPIXEL="$path/dimpixel.png"
    RES_COLOR="$path/color.png"

    # resize
    base_resize "$base" "$RES_RESIZE" "$resolution"

    # effects
    for effect in "${fx_list[@]}"; do
        case $effect in
            dim) fx_dim "$RES_RESIZE" "$RES_DIM";;
            blur) fx_blur "$RES_RESIZE" "$RES_BLUR" "$resolution";;
            dimblur) fx_dimblur "$RES_RESIZE" "$RES_DIMBLUR" "$resolution";;
            pixel) fx_pixel "$RES_RESIZE" "$RES_PIXEL";;
            dimpixel) fx_dimpixel "$RES_RESIZE" "$RES_DIMPIXEL";;
            color) fx_color "$RES_COLOR" "$resolution";;
        esac
    done

}

# apply resize
base_resize() {
    local input="$1"
    local output="$2"
    local size="$3"

    echof act "Resizing base image..."
    eval $convert_command "$input" \
        -resize "$size""^" \
        -gravity center \
        -extent "$size" \
        "$output"
}

# apply dim
fx_dim() {
    local input="$1"
    local output="$2"

    echof act "Rendering 'dim' effect..."
    eval $convert_command "$input" \
        -fill black -colorize "$dim_level"% \
        "$output"
}

# apply blur
fx_blur() {
    local input="$1"
    local output="$2"
    local size="$3"

    echof act "Rendering 'blur' effect..."
    blur_shrink=$(echo "scale=2; 20 / $blur_level" | bc)
    blur_sigma=$(echo "scale=2; 0.6 * $blur_level" | bc)
    eval $convert_command "$input" \
        -filter Gaussian \
        -resize "$blur_shrink%" \
        -define "filter:sigma=$blur_sigma" \
        -resize "$size^" -gravity center -extent "$size" \
        "$output"
}

# apply dimblur
fx_dimblur() {
    local input="$1"
    local output="$2"
    local size="$3"

    echof act "Rendering 'dimblur' effect..."
    blur_shrink=$(echo "scale=2; 20 / $blur_level" | bc)
    blur_sigma=$(echo "scale=2; 0.6 * $blur_level" | bc)
    eval $convert_command "$input" \
        -fill black -colorize "$dim_level"% \
        -filter Gaussian \
        -resize "$blur_shrink%" \
        -define "filter:sigma=$blur_sigma" \
        -resize "$size^" -gravity center -extent "$size" \
        "$output"
}

# pixelate
fx_pixel() {
    local input="$1"
    local output="$2"

    echof act "Rendering 'pixel' effect..."
    IFS=',' read -ra range <<< "$pixel_scale"
    eval $convert_command "$input" \
        -scale "${range[0]}"% -scale "${range[1]}"% \
        "$output"
}

# apply dimpixel
fx_dimpixel() {
    local input="$1"
    local output="$2"

    echof act "Rendering 'dimpixel' effect..."
    IFS=',' read -ra range <<< "$pixel_scale"
    eval $convert_command "$input" \
        -fill black -colorize "$dim_level"% \
        -scale "${range[0]}"% -scale "${range[1]}"% \
        "$output"
}

# create solid color
fx_color() {
    local output="$1"
    local size="$2"

    echof act "Rendering 'color' effect..."
    eval $convert_command -size "$size" canvas:\#"$solid_color" "$RES_COLOR"
}

# create loginbox rectangle, set "$RECTANGLE"
create_loginbox () {
    RECTANGLE="$CUR_DIR/rectangle.png"
    local shadow="$CUR_DIR/shadow.png"
    local width height
    width=$(logical_px 340 1)
    height=$(logical_px 100 2)
    $convert_command -size "$width"x"$height" xc:\#"$loginbox" -fill none "$RECTANGLE"
    $convert_command "$RECTANGLE" \
        \( -clone 0 -background \#"$loginshadow" -shadow 100x5+0+0 \) +swap \
        -background none -layers merge +repage "$shadow"
    $composite_command -compose Dst_Out -gravity center \
        "$RECTANGLE" "$shadow" -alpha Set "$shadow"
    $convert_command "$shadow" "$RECTANGLE" -geometry +10+10 -composite "$RECTANGLE"
    [[ "$shadow" ]] && rm "$shadow"
}

# create rectangle with description, set "$DESCRECT"
create_description () {
    DESCRECT="$CUR_DIR/description.png"
    local shadow="$CUR_DIR/shadow.png"
    $convert_command -background none -family "$(fc-match "$font" family)" -style Normal -pointsize 14 -fill \#"$greetercolor" label:"\ $description\ " -bordercolor \#"$loginbox" -border 10 "$DESCRECT"
    $convert_command "$DESCRECT" \
        \( -clone 0 -background \#"$loginshadow" -shadow 100x5+0+0 \) +swap \
        -background none -layers merge +repage "$shadow"
    $composite_command -compose Dst_Out -gravity center \
        "$DESCRECT" "$shadow" -alpha Set "$shadow"
    $convert_command "$shadow" "$DESCRECT" -geometry +10+10 -composite "$DESCRECT"
    [[ "$shadow" ]] && rm "$shadow"
}

# delete and recreate directory
purge_cache () {
    if [[ -d "$1" ]]; then
        rm -r "$1"
    fi
    mkdir -p "$1"
}

# update lockscreen and wallpaper images
update () {
    local images=("$@")

    echof act "Updating image cache..."
    mkdir -p "$CACHE_DIR" &>/dev/null

    get_display_list # DISPLAY_LIST
    get_total_size # TOTAL_SIZE
    echof info "Detected ${#DISPLAY_LIST[@]} display(s) @ $TOTAL_SIZE total resolution"

    get_wall_list "${images[@]}" # WALL_LIST
    echof info "Original image(s): ${WALL_LIST[*]##*/}"

    # Prepare description box to obtain width for positioning
    local descwidth
    local descheight
    if [ -z "$description" ]; then
        descwidth=0
        descheight=0
    else
        create_description
        descwidth=$(identify -format "%[fx:w]" "$DESCRECT")
        descheight=$(identify -format "%[fx:h]" "$DESCRECT")
    fi

    for ((i=0; i<${#DISPLAY_LIST[@]}; i++)); do
        display="${DISPLAY_LIST[$i]}"
        USER_WALL="${WALL_LIST[$i]}"

        # escape spaces for IM
        if echo "$USER_WALL" | grep -E -q "[[:space:]]"; then
            USER_WALL="${USER_WALL// /\\ }"
        fi

        IFS=' ' read -r -a dinfo  <<< "$display"
        local id="${dinfo[0]}"
        local device="${dinfo[1]}"
        local geometry="${dinfo[2]}"

        read -r -a cols <<< "${geometry//[x+-]/ }"
        local position="${geometry#*"${cols[1]}"}"
        local resolution="${geometry%"${position}"*}"

        if [[ $id -eq "$display_on" ]] || [[ "$display_on" -eq 0 ]]; then

            IFS='x' read -r -a dimension <<< "$resolution"
            res_x="${dimension[0]}"
            res_y="${dimension[1]}"
            read -r -a val <<< "${position//[+-]/ }"
            read -r -a sym <<< "${position//[0-9]/ }"
            pos_x="${sym[0]}${val[0]}"
            pos_y="${sym[1]}${val[1]}"

            rect_x=$((pos_x + $(logical_px 15 1)))
            rect_y=$((pos_y + res_y - $(logical_px 140 2)))
            positions+=("+$((rect_x))+$((rect_y))")

            descrect_x=$((pos_x + res_x - descwidth - $(logical_px 15 1)))
            descrect_y=$((pos_y + res_y - descheight - $(logical_px 20 2)))
            positions_desc+=("+$((descrect_x))+$((descrect_y))")
        fi

        local path="$CACHE_DIR/$id-$device"
        purge_cache "$path"

        if [ "$span_image" = true ]; then
            if [ "$id" -gt 1 ]; then
                continue
            else
                device="[span]"
                id="*"
                resolution="$TOTAL_SIZE"
            fi
        fi

        echof info "Processing display: $device ($id)"
        echof info "Resolution: $resolution"

        if [ "$span_image" = true ]; then
            resize_and_render "$USER_WALL" "$path" "$resolution"
        else
            resize_and_render "$USER_WALL" "$path" "$resolution"

            PARAM_RESIZE="$PARAM_RESIZE $RES_RESIZE -geometry $position -composite "
            PARAM_DIM="$PARAM_DIM $RES_DIM -geometry $position -composite "
            PARAM_BLUR="$PARAM_BLUR $RES_BLUR -geometry $position -composite "
            PARAM_DIMBLUR="$PARAM_DIMBLUR $RES_DIMBLUR -geometry $position -composite "
            PARAM_PIXEL="$PARAM_PIXEL $RES_PIXEL -geometry $position -composite "
            PARAM_DIMPIXEL="$PARAM_DIMPIXEL $RES_DIMPIXEL -geometry $position -composite "
            PARAM_COLOR="$PARAM_COLOR $RES_COLOR -geometry $position -composite "
        fi

    done

    purge_cache "$CUR_DIR"

    if [ "$span_image" = true ] || [ ${#DISPLAY_LIST[@]} -lt 2 ]; then
        echof act "Rendering final wallpaper images..."
        [[ -f "$RES_RESIZE" ]] && eval "cp $RES_RESIZE $CUR_W_RESIZE"
        [[ -f "$RES_DIM" ]] && eval "cp $RES_DIM $CUR_W_DIM"
        [[ -f "$RES_BLUR" ]] && eval "cp $RES_BLUR $CUR_W_BLUR"
        [[ -f "$RES_DIMBLUR" ]] && eval "cp $RES_DIMBLUR $CUR_W_DIMBLUR"
        [[ -f "$RES_PIXEL" ]] && eval "cp $RES_PIXEL $CUR_W_PIXEL"
        [[ -f "$RES_DIMPIXEL" ]] && eval "cp $RES_DIMPIXEL $CUR_W_DIMPIXEL"
        [[ -f "$RES_COLOR" ]] && eval "cp $RES_COLOR $CUR_W_COLOR"
    else
        echof act "Creating canvas: $TOTAL_SIZE"
        [[ -f "$RES_RESIZE" ]] && eval "$convert_command -size $TOTAL_SIZE 'xc:blue' $CUR_W_RESIZE"
        [[ -f "$RES_DIM" ]] && eval "$convert_command -size $TOTAL_SIZE 'xc:blue' $CUR_W_DIM"
        [[ -f "$RES_BLUR" ]] && eval "$convert_command -size $TOTAL_SIZE 'xc:blue' $CUR_W_BLUR"
        [[ -f "$RES_DIMBLUR" ]] && eval "$convert_command -size $TOTAL_SIZE 'xc:blue' $CUR_W_DIMBLUR"
        [[ -f "$RES_PIXEL" ]] && eval "$convert_command -size $TOTAL_SIZE 'xc:blue' $CUR_W_PIXEL"
        [[ -f "$RES_DIMPIXEL" ]] && eval "$convert_command -size $TOTAL_SIZE 'xc:blue' $CUR_W_DIMPIXEL"
        [[ -f "$RES_COLOR" ]] && eval "$convert_command -size $TOTAL_SIZE 'xc:blue' $CUR_W_COLOR"

        echof act "Rendering final wallpaper images..."
        [[ -f "$CUR_W_RESIZE" ]] && eval "$convert_command $CUR_W_RESIZE $PARAM_RESIZE $CUR_W_RESIZE"
        [[ -f "$CUR_W_DIM" ]] && eval "$convert_command $CUR_W_DIM $PARAM_DIM $CUR_W_DIM"
        [[ -f "$CUR_W_BLUR" ]] && eval "$convert_command $CUR_W_BLUR $PARAM_BLUR $CUR_W_BLUR"
        [[ -f "$CUR_W_DIMBLUR" ]] && eval "$convert_command $CUR_W_DIMBLUR $PARAM_DIMBLUR $CUR_W_DIMBLUR"
        [[ -f "$CUR_W_PIXEL" ]] && eval "$convert_command $CUR_W_PIXEL $PARAM_PIXEL $CUR_W_PIXEL"
        [[ -f "$CUR_W_DIMPIXEL" ]] && eval "$convert_command $CUR_W_DIMPIXEL $PARAM_DIMPIXEL $CUR_W_DIMPIXEL"
        [[ -f "$CUR_W_COLOR" ]] && eval "$convert_command $CUR_W_COLOR $PARAM_COLOR $CUR_W_COLOR"
    fi

    echof act "Rendering final lockscreen images..."

    create_loginbox
    for pos in "${positions[@]}"; do
        PARAM_RECT="$PARAM_RECT $RECTANGLE -geometry $pos -composite "
    done

    if [ -n "$description" ]; then
        create_description
        for descpos in "${positions_desc[@]}"; do
            PARAM_RECT="$PARAM_RECT $DESCRECT -geometry $descpos -composite "
        done
    fi

    [[ -f "$CUR_W_RESIZE" ]] && eval "$convert_command $CUR_W_RESIZE $PARAM_RECT $CUR_L_RESIZE"
    [[ -f "$CUR_W_DIM" ]] && eval "$convert_command $CUR_W_DIM $PARAM_RECT $CUR_L_DIM"
    [[ -f "$CUR_W_BLUR" ]] && eval "$convert_command $CUR_W_BLUR $PARAM_RECT $CUR_L_BLUR"
    [[ -f "$CUR_W_DIMBLUR" ]] && eval "$convert_command $CUR_W_DIMBLUR $PARAM_RECT $CUR_L_DIMBLUR"
    [[ -f "$CUR_W_PIXEL" ]] && eval "$convert_command $CUR_W_PIXEL $PARAM_RECT $CUR_L_PIXEL"
    [[ -f "$CUR_W_DIMPIXEL" ]] && eval "$convert_command $CUR_W_DIMPIXEL $PARAM_RECT $CUR_L_DIMPIXEL"
    [[ -f "$CUR_W_COLOR" ]] && eval "$convert_command $CUR_W_COLOR $PARAM_RECT $CUR_L_COLOR"

    [[ "$RECTANGLE" ]] && rm "$RECTANGLE"
    [[ "$DESCRECT" ]] && rm "$DESCRECT"

    echof ok "Done"

}

# set wallpaper with effect
wallpaper() {
    local effect="$1"

    # make wallpaper span displays
    get_display_list
    if [ "$span_image" = true ] || [[ "${#DISPLAY_LIST[@]}" -gt 1 ]]; then
        wallpaper_cmd="$wallpaper_cmd --no-xinerama"
    fi

    # set wallpaper
    case "$effect" in
        dim) wallpaper="$CUR_W_DIM";;
        blur) wallpaper="$CUR_W_BLUR";;
        dimblur) wallpaper="$CUR_W_DIMBLUR";;
        pixel) wallpaper="$CUR_W_PIXEL";;
        dimpixel) wallpaper="$CUR_W_DIMPIXEL";;
        color) wallpaper="$CUR_W_COLOR";;
        *) wallpaper="$CUR_W_RESIZE";;
    esac
    eval "$wallpaper_cmd $wallpaper"
}

# wrap echo with fancy prefix
echof() {
    local prefix="$1"
    local message="$2"

    case "$prefix" in
        header) msgpfx="[\e[1;95mB\e[m]";;
        info) msgpfx="[\e[1;97m=\e[m]";;
        act) msgpfx="[\e[1;92m*\e[m]";;
        ok) msgpfx="[\e[1;93m+\e[m]";;
        error) msgpfx="[\e[1;91m!\e[m]";;
        *) msgpfx="";;
    esac
    [ "$quiet" != true ] && echo -e "$msgpfx $message"
}

# help message
usage() {
    echo
    echo "Usage: betterlockscreen [-u <PATH>] [-l <EFFECT>] [-w <EFFECT>]"
    echo
    echo "  -q --quiet"
    echo "      Do not produce any text output on locking"
    echo
    echo "  -u --update <PATH>"
    echo "      Update lock screen image"
    echo
    echo "  -l --lock <EFFECT>"
    echo "      Lock screen with cached image"
    echo
    echo "  -w --wall <EFFECT>"
    echo "      Set wallpaper with cached image"
    echo
    echo "Additional arguments:"
    echo
    echo "  --display <N>"
    echo "      Set display to draw loginbox"
    echo
    echo "  --span"
    echo "      Scale image to span multiple displays"
    echo
    echo "  --off <N>"
    echo "      Turn display off after N seconds"
    echo
    echo "  --fx <EFFECT,EFFECT,EFFECT>"
    echo "      List of effects to generate"
    echo
    echo "  --desc <DESCRIPTION>"
    echo "      Set a description for the new lock screen image"
    echo "      (Only has an effect in combination with --update)"
    echo
    echo "  --show-layout"
    echo "      Show current keyboard layout"
    echo
    echo "  --wallpaper-cmd <command>"
    echo "      to set your custom wallpaper setter"
    echo
    echo "  --time-format <format>"
    echo "      to set the time format used by i3lock-color"
    echo
    echo "  -- <ARGS>"
    echo "      Pass additional arguments to i3lock"
    echo
    echo "Effects arguments:"
    echo
    echo "  --dim <N>"
    echo "      Dim image N percent (0-100)"
    echo
    echo "  --blur <N>"
    echo "      Blur image N amount (0.0-1.0)"
    echo
    echo "  --pixel <N,N>"
    echo "      Pixelate image with N shrink and N grow (unsupported)"
    echo
    echo "  --color <HEX>"
    echo "      Solid color background with HEX"
    echo
    exit 1
}

lockargs=(-n)

init_config

# show usage when no arguments passed
[[ "$1" = "" ]] && usage

# process arguments
for arg in "$@"; do
    [[ "${arg:0:1}" = '-' ]] || continue

    case "$1" in

        -q | --quiet)
            quiet=true
            shift
            ;;

        -u | --update)
            runupdate=true
            imagepaths+=("$2")
            shift 2
            ;;

        -s | --suspend)
            runsuspend=true
            ;&

        -l | --lock)
            runlock=true
            if [[ ${2:0:1} = '-' ]]; then
                shift 1
            else
                lockstyle="$2"; shift 2
            fi
            ;;

        -w | --wall)
            wallpaper "$2"
            shift 2
            ;;

        --wallpaper-cmd)
            wallpaper_cmd="$2"
            shift 2
            ;;

        --time-format)
            time_format="$2"
            shift 2
            ;;

        --display)
            display_on="$2"
            shift 2
            ;;

        --span)
            span_image=true
            shift 1
            ;;

        --off)
            lock_timeout="$2"
            shift 2
            ;;

        --text)
            locktext="$2"
            shift 2
            ;;

        --show-layout)
            keylayout="$2";
            lockargs+=(--keylayout "${keylayout:-0}")
            shift 2
            ;;

        --fx)
            IFS=',' read -ra fx_list <<< "$2"
            shift 2
            ;;

        --dim)
            dim_level="$2"
            shift 2
            ;;

        --blur)
            blur_level="$2"
            shift 2
            ;;

        --pixel)
            pixel_scale="$2"
            shift 2
            ;;

        --color)
            solid_color="${2//\#/}"
            shift 2
            ;;

        --desc)
            description="$2"
            shift 2
            ;;

        -v | --version)
            echo
            echo "Betterlockscreen: version: v$VERSION (dunst: $DUNST_INSTALLED, feh: $FEH_INSTALLED)"
            $i3lockcolor_bin --version
            $convert_command --version

            if [[ "$DUNST_INSTALLED" == "true" ]]; then
                dunstctl debug
            fi

            if [[ "$FEH_INSTALLED" == "true" ]]; then
                feh --version
            fi

            break
            ;;

        --)
            lockargs+=("${@:2}")
            break
            ;;

        -h | --help | *)
            usage
            ;;
    esac
done

echof header "Betterlockscreen"

# Run image generation
[[ $runupdate ]] && update "${imagepaths[@]}"

# Activate lockscreen
[[ $runlock ]] && lockinit "$lockstyle"

exit 0
