#!/bin/bash
# Generate tmux, ghostty and (from ghostty-format sources) alacritty color
# files, so bin/theme can use any theme with `theme --set <mode> <name>`.
#
# Sources: the alacritty-theme checkout (~/src/alacritty/alacritty-theme/
# themes) by name, or any theme file by path -- alacritty-format .toml, or the
# `palette = N=#hex` format Ghostty.app bundles its built-in themes in.
#
# Writes:
#   $XDG_CONFIG_HOME/tmux/colors/<name>.conf
#   $XDG_CONFIG_HOME/ghostty/colors/<name>
#   $XDG_CONFIG_HOME/alacritty/colors/<name>.toml   (ghostty-format sources
#                                                     only: an alacritty-format
#                                                     source is used in place)
#
# Usage:
#   theme-gen <name>              # theme from the alacritty-theme checkout
#   theme-gen /path/to/theme.toml # or a ghostty theme file
#   theme-gen -n <slug> <path>    # generate as <slug>, not the basename
#   theme-gen -f <name>           # overwrite existing generated files
#
# Nothing is activated: run `theme --set <mode> <name>` afterwards.

set -euo pipefail

: ${HOME:="$(cd ~ && pwd)"}
: ${XDG_CONFIG_HOME:=$HOME/.config}
themes_dir="$HOME/src/alacritty/alacritty-theme/themes"
ghostty_themes_dir="/Applications/Ghostty.app/Contents/Resources/ghostty/themes"

usage() {
  sed -n '2,19p' "$0" | sed 's/^# \{0,1\}//'
  exit "${1:-0}"
}

force=0
while getopts :fn:h opt; do
  case "$opt" in
    f) force=1 ;;
    n) override_name="$OPTARG" ;;
    h) usage 0 ;;
    *) usage 1 ;;
  esac
done
shift $((OPTIND - 1))

if [[ $# -ne 1 ]]; then
  usage 1
fi

src="$1"
if [[ -f "$src" ]]; then
  name="$(basename "$src")"
  name="${name%.toml}"
else
  name="$src"
  src="${themes_dir}/${name}.toml"
fi
if [[ -n "${override_name:-}" ]]; then
  name="$override_name"
fi

if [[ ! -f "$src" ]]; then
  echo "ERROR: no theme file at $src" >&2
  echo "Themes available in $themes_dir:" >&2
  ls "$themes_dir" >&2 2>/dev/null || true
  echo "Ghostty built-ins live in $ghostty_themes_dir" >&2
  exit 1
fi

# Ghostty bundles themes as `palette = N=#hex` plus background/foreground;
# alacritty themes are .toml sections. Parse either into the same
# <section>.<key>=<hex> lines (no leading '#', lowercased) the writers below
# consume.
parse_colors() {
  if grep -qE '^[ \t]*palette[ \t]*=' "$src"; then
    awk '
      function hexof(val) {
        if (match(val, /#[0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f]/))
          return tolower(substr(val, RSTART + 1, RLENGTH - 1))
        return ""
      }
      BEGIN { split("black red green yellow blue magenta cyan white", K, " ") }
      /^[ \t]*palette[ \t]*=/ {
        line = $0
        sub(/^[ \t]*palette[ \t]*=[ \t]*/, "", line)
        split(line, kv, "=")
        idx = kv[1] + 0
        hex = tolower(kv[2]); gsub(/[ \t]/, "", hex); sub(/^#/, "", hex)
        if (hex !~ /^[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]$/) next
        if (idx < 0 || idx > 15) next
        print ((idx < 8) ? "normal" : "bright") "." K[(idx % 8) + 1] "=" hex
        next
      }
      /^[ \t]*background[ \t]*=/ { hex = hexof($0); if (hex != "") print "primary.background=" hex; next }
      /^[ \t]*foreground[ \t]*=/ { hex = hexof($0); if (hex != "") print "primary.foreground=" hex; next }
    ' "$src"
  else
    awk '
      /^[ \t]*\[/ {
        section = $0
        sub(/^[ \t]*\[/, "", section); sub(/\].*$/, "", section)
        next
      }
      /[ \t]*=/ {
        sect = section
        sub(/^colors\./, "", sect)
        if (sect != "primary" && sect != "normal" && sect != "bright") next
        eq = index($0, "=")
        key = substr($0, 1, eq - 1); gsub(/[ \t]/, "", key)
        val = substr($0, eq + 1)
        if (match(val, /"[^"]*"|'"'"'[^'"'"']*'"'"'/)) {
          v = substr(val, RSTART + 1, RLENGTH - 2)
        } else if (match(val, /#[0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f]/)) {
          v = substr(val, RSTART, RLENGTH)
        } else next
        gsub(/[ \t]/, "", v)
        if (v !~ /^#?[0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f]$/) next
        sub(/^#/, "", v)
        print sect "." key "=" tolower(v)
      }
    ' "$src"
  fi
}

# Ghostty-format sources have no alacritty file of their own, so theme-gen
# writes one; alacritty-format sources are used in place, so none is written.
ghostty_source=0
if grep -qE '^[ \t]*palette[ \t]*=' "$src"; then
  ghostty_source=1
fi

# no associative arrays: /bin/bash on macOS is 3.2. Keys are sanitized into
# variable names like c_primary_background.
c_key() {
  printf '%s' "$1" | tr '.' '_'
}
while IFS='=' read -r k v; do
  # keys come from the theme file and go through eval: accept only plain
  # section.key names and hex values, or a theme file could run commands
  [[ "$k" =~ ^[a-z]+\.[a-z_]+$ && "$v" =~ ^[0-9a-f]{6}$ ]] || continue
  eval "c_$(c_key "$k")='$v'"
done < <(parse_colors)

get() {
  local var="c_$(c_key "$1")" fallback="${2-}"
  if [[ -n "${!var:-}" ]]; then
    printf '%s' "${!var}"
  elif [[ -n "$fallback" ]]; then
    printf '%s' "$fallback"
  else
    echo "ERROR: theme is missing $1" >&2
    exit 1
  fi
}

bg="$(get primary.background)"
fg="$(get primary.foreground)"

tmux_out="$XDG_CONFIG_HOME/tmux/colors/${name}.conf"
ghostty_out="$XDG_CONFIG_HOME/ghostty/colors/${name}"
alacritty_out="$XDG_CONFIG_HOME/alacritty/colors/${name}.toml"
for out in "$tmux_out" "$ghostty_out" $([[ "$ghostty_source" -eq 1 ]] && echo "$alacritty_out"); do
  if [[ -e "$out" && "$force" -ne 1 ]]; then
    echo "ERROR: $out already exists; use -f to overwrite" >&2
    exit 1
  fi
done

if [[ "$ghostty_source" -eq 1 ]]; then
  {
    echo "# ${name} (generated by theme-gen from ${src})"
    echo "[colors.primary]"
    echo "background = \"#${bg}\""
    echo "foreground = \"#${fg}\""
    echo
    echo "[colors.normal]"
    for key in black red green yellow blue magenta cyan white; do
      echo "${key} = \"#$(get "normal.${key}")\""
    done
    echo
    echo "[colors.bright]"
    for key in black red green yellow blue magenta cyan white; do
      echo "${key} = \"#$(get "bright.${key}")\""
    done
  } > "$alacritty_out"
fi

# Ghostty: primary + full 16-color palette.
{
  echo "# ${name} color scheme (generated by theme-gen from ${src})"
  echo
  echo "background = #${bg}"
  echo "foreground = #${fg}"
  echo
  local_i=0
  for section in normal bright; do
    for key in black red green yellow blue magenta cyan white; do
      echo "palette = ${local_i}=#$(get "${section}.${key}" "$(get "normal.${key}" "${fg}")")"
      local_i=$((local_i + 1))
    done
  done
} > "$ghostty_out"

# tmux: status bar, messages, pane borders. tmux >= 3.2 takes hex directly.
pane_border="$(get bright.black "$(get normal.black "${fg}")")"
pane_active="$(get normal.blue "${fg}")"
mkdir -p "$(dirname "$tmux_out")"
{
  echo "# ${name} (generated by theme-gen from ${src})"
  echo "set -g status-style fg=#${fg},bg=default"
  echo "set -g message-style fg=#${bg},bg=#${fg},fill=#${fg}"
  echo "set -g pane-border-style fg=#${pane_border}"
  echo "set -g pane-active-border-style fg=#${pane_active}"
  echo "set -g window-status-current-style fg=#${fg}"
} > "$tmux_out"

echo "wrote $tmux_out"
echo "wrote $ghostty_out"
if [[ "$ghostty_source" -eq 1 ]]; then
  echo "wrote $alacritty_out"
fi
echo "activate with: theme --set <mode> ${name}"
