#!/bin/bash

set -euo pipefail

cs_dir="/etc/tty-colorscheme/colorschemes"

_set_colors() {
    # Black   Red     Green   Yellow  Blue    Magenta Cyan    White
    # color01 color02 color03 color04 color05 color06 color07 color08
    # color09 color10 color11 color12 color13 color14 color15 color16

    printf "\e]P0%s\e]P1%s\e]P2%s\e]P3%s\e]P4%s\e]P5%s\e]P6%s\e]P7%s" \
        "${color01:?}" "${color02:?}" "${color03:?}" "${color04:?}" \
        "${color05:?}" "${color06:?}" "${color07:?}" "${color08:?}"

    printf "\e]P8%s\e]P9%s\e]PA%s\e]PB%s\e]PC%s\e]PD%s\e]PE%s\e]PF%s" \
        "${color09:?}" "${color10:?}" "${color11:?}" "${color12:?}" \
        "${color13:?}" "${color14:?}" "${color15:?}" "${color16:?}"
}

_fnt_warn() {
    [[ -z "${fnt_arr:-}" ]] && printf "No PSF fonts found\n"
}

_get_fonts() { # Only output file name
    fnt_dirs=(
        "/usr/share/consolefonts"
        "/usr/share/kbd/consolefonts"
        "/usr/lib/kbd/consolefonts"
    )

    for fnt_dir in "${fnt_dirs[@]}"; do
        if [[ -d "${fnt_dir}" ]]; then
            find "${fnt_dir}" -name "*.psf*" -exec file {} \; \
                | awk -F: '/gzip|PC Screen Font/ {print $1}' \
                | awk -F/ '{print $NF}' \
                | sort -V
        fi
    done
}

_set_font() {
    fnt="${1}"

    if [[ "$(id -u)" -eq 0 ]]; then
        for i in {1..6}; do
            setfont "${fnt}" -C "/dev/tty${i}"
        done
    else
        setfont "${fnt}"
    fi

    clear
    showconsolefont

    printf "Setting font: %s\n\n" "${fnt%%.*}"
}

_cs_warn() {
    [[ -z "${cs_arr:-}" ]] && printf "No colorschemes found\n"
}

_get_schemes() { # Only output file name
    find "${cs_dir}"/* | awk -F/ '{print $NF}'
}

cs_list_schemes() {
    mapfile -t cs_arr < <(_get_schemes)
    _cs_warn && exit 1
    printf "%s\n" "${cs_arr[@]}"
}

cs_list_fonts() {
    mapfile -t fnt_arr < <(_get_fonts)
    _fnt_warn && exit 1
    printf "%s\n" "${fnt_arr[@]%%.*}"
}

cs_print_palette() {
    cnt=1
    for bkgrnd in {40..47}; do
        printf "\e[%sm" "${bkgrnd}"
        for bold in 22 1; do
            printf "\e[%sm" "${bold}"
            for frgrnd in {30..37}; do
                printf "\e[%sm %3s" "${frgrnd}" "${cnt}"
                ((cnt++))
            done
        done
        printf "\e[0m\n"
    done
}

cs_cycle() {
    mapfile -t cs_arr < <(_get_schemes)
    max_ind_cs="$((${#cs_arr[@]}-1))"
    ind_cs=-1

    mapfile -t fnt_arr < <(_get_fonts)
    max_ind_fnt="$((${#fnt_arr[@]}-1))"
    ind_fnt=-1

    mode=""
    clear

    while true; do
        case "${mode}" in
            "cs")
                # Only process if index is valid
                if [[ "${ind_cs}" -ge 0 && "${ind_cs}" -le "${max_ind_cs}" ]]; then
                    cs_set_scheme "${cs_arr[${ind_cs}]}"
                    printf "[%s/%s]\n" "$((ind_cs+1))" "$((max_ind_cs+1))"
                else
                    clear
                    _cs_warn
                fi
                ;;
            "fnt")
                # Only process if index is valid
                if [[ "${ind_fnt}" -ge 0 && "${ind_fnt}" -le "${max_ind_fnt}" ]]; then
                    _set_font "${fnt_arr[${ind_fnt}]}"
                    printf "[%s/%s]\n" "$((ind_fnt+1))" "$((max_ind_fnt+1))"
                else
                    clear
                    _fnt_warn
                fi
                ;;
        esac

        printf "\nUse j/k keys to cycle through colorschemes\n"
        printf "Use h/l keys to cycle through fonts\n"

        read -rsn1
        case "${REPLY}" in
            j)
                mode="cs"
                if [[ "${ind_cs}" -lt "${max_ind_cs}" ]]; then
                    ((ind_cs++)) || true # Ignore arithmetic errors
                else
                    ind_cs=0 # Wrap around to the first element
                fi
                ;;
            k)
                mode="cs"
                if [[ "${ind_cs}" -gt 0 ]]; then
                    ((ind_cs--))
                elif [[ "${ind_cs}" == -1 ]]; then
                    ind_cs=0 # Get to the first element if none is selected
                else
                    ind_cs="${max_ind_cs}" # Wrap around to the last element
                fi
                ;;
            l)
                mode="fnt"
                if [[ "${ind_fnt}" -lt "${max_ind_fnt}" ]]; then
                    ((ind_fnt++)) || true # Ignore arithmetic errors
                else
                    ind_fnt=0 # Wrap around to the first element
                fi
                ;;
            h)
                mode="fnt"
                if [[ "${ind_fnt}" -gt 0 ]]; then
                    ((ind_fnt--))
                elif [[ "${ind_fnt}" == -1 ]]; then
                    ind_fnt=0 # Get to the first element if none is selected
                else
                    ind_fnt="${max_ind_fnt}" # Wrap around to the last element
                fi
                ;;
            *)
                exit 0
                ;;
        esac
    done
}

cs_set_scheme() {
    if [[ ! -f "${cs_dir}/${1}" ]]; then
        printf "Invalid colorscheme: %s\n" "${1}"
        exit 1
    fi

    while IFS="" read -r line || [[ -n "${line}" ]]; do
        if [[ "${line}" =~ ^(color(0[1-9]|1[0-6]))=\"([0-9A-Fa-f]{6})\"$ ]]; then
            declare -g "${BASH_REMATCH[1]}=${BASH_REMATCH[3]}"
        else
            [[ -z "${line}" || "${line}" =~ ^[[:space:]]*# ]] && continue
            printf "Invalid color format: %s in %s\n" "${line}" "${1}"
            exit 1
        fi
    done < "${cs_dir}/${1}"

    if [[ "$(id -u)" -eq 0 ]]; then
        for i in {1..6}; do
            _set_colors > "/dev/tty${i}"
        done
    else
        _set_colors
    fi

    # Prevent palette going into logs
    if [[ "${TERM}" == "linux" ]]; then
        clear
        cs_print_palette
    fi

    printf "\nSetting colorscheme: %s\n\n" "${1}"
}

cs_print_help() {
    printf "Usage: %s [option | colorscheme]\n" "$(basename "${0}")"
    printf "  -l    List available colorschemes\n"
    printf "  -f    List available PSF fonts\n"
    printf "  -p    Print the current palette\n"
    printf "  -c    Cycle through colorschemes and fonts\n"

    if ! tty | grep -q tty; then
        printf "%s should be run from TTY\n" "$(basename "${0}")"
    fi
}

while getopts ":lfpc" opt; do
    case "${opt}" in
        l)
            cs_list_schemes
            exit 0
            ;;
        f)
            cs_list_fonts
            exit 0
            ;;
        p)
            cs_print_palette
            exit 0
            ;;
        c)
            cs_cycle
            exit 0
            ;;
        *)
            printf "Invalid option: -%s\n" "${OPTARG}"
            cs_print_help
            exit 1
            ;;
    esac
done

if [[ "${#}" -eq 1 ]]; then
    cs_set_scheme "${1}"
    exit 0
fi

cs_print_help
