#!/bin/bash
# Generates duotone masters for every icon already resolved in
# generated_icons/manifest.json, colorless/cacheable, two kinds:
#
#  - SVG-sourced icons (the common case: kora's own art) get a PURE VECTOR
#    master at generated_icons/duotone_vector/<name>.svg -- each drawable
#    element's fill replaced with a lightness placeholder ("__L0.734__"),
#    see lib/icon_duotone_vector.py's `tag`. This is the preferred form:
#    an embedded-raster-in-SVG approach was tried first and rendered fine
#    standalone (rsvg-convert, GdkPixbuf) but silently failed in rofi's own
#    icon list -- real vector shapes render everywhere.
#  - PNG-sourced icons (no vector source to recolor) fall back to a
#    grayscale-with-alpha RASTER master at generated_icons/duotone/<name>.png
#    (see lib/icon_remote.py's `grayscale`), colorized via -clut at deploy
#    time instead.
#
# Applying the current accent's dark/light tonal pair to either cached
# master (15-deploy-duotone-theme.sh) recolors each icon by its original
# lightness/shading, instead of reducing it to a flat single-color
# silhouette -- see the comparison this was built from:
# https://claude.ai/code/artifact/898c6a99-d891-498c-91ed-57406c6086ab
#
# Deliberately reuses the manifest 10-generate-app-icons.sh already built
# (source_path/source_theme/source_relpath/is_status per icon) rather than
# re-resolving candidates from scratch -- run 10 first (or after installing
# a new app) if an icon isn't in the manifest yet.
#
# Like 10, this is the one-time expensive step, cached into this repo so a
# brand-new install never needs to re-run it; only
# 15-deploy-duotone-theme.sh (cheap: just the color substitution) needs to
# run there.
#
# Usage: 14-generate-duotone-icons.sh --vm [--force <icon-name> ...] [--force-all] [--size N]
#   --vm only -- it only ever has new work to do for manifest entries 10
#   just added, and 10 is --vm only (see its usage comment), so running
#   this against a laptop would just re-process icons already cached here.
set -euo pipefail
cd "$(dirname "${BASH_SOURCE[0]}")"
source ./00-config.sh

SIZE=256
FORCE_NAMES=()
FORCE_ALL=0
parse_target_flag "$@"
set -- "${PARSED_ARGS[@]+"${PARSED_ARGS[@]}"}"
while [ $# -gt 0 ]; do
    case "$1" in
        --size) shift; SIZE="$1" ;;
        --force-all) FORCE_ALL=1 ;;
        --force) shift; FORCE_NAMES+=("$1") ;;
        *) echo "Unknown argument: $1" >&2; exit 1 ;;
    esac
    shift
done

if [ "$TARGET" = "local" ]; then
    echo "14-generate-duotone-icons.sh is --vm only -- see the usage comment at the top" >&2
    echo "of this file for why. Run against --vm instead." >&2
    exit 1
fi

GEN_DIR="../generated_icons"
MANIFEST="${GEN_DIR}/manifest.json"
RASTER_DIR="${GEN_DIR}/duotone"
VECTOR_DIR="${GEN_DIR}/duotone_vector"
mkdir -p "$RASTER_DIR" "$VECTOR_DIR"

if [ ! -f "$MANIFEST" ]; then
    echo "No ${MANIFEST} -- run ./10-generate-app-icons.sh first to resolve candidates." >&2
    exit 1
fi

WORK=$(mktemp -d)
trap 'rm -rf "$WORK"' EXIT

echo ">>> Target: ${TARGET}"

FORCE_NAMES_JSON=$(printf '%s\n' "${FORCE_NAMES[@]+"${FORCE_NAMES[@]}"}" | python3 -c "import json,sys; print(json.dumps([l.strip() for l in sys.stdin if l.strip()]))")

python3 -c "
import json, os
manifest = json.load(open('${MANIFEST}'))
force_all = ${FORCE_ALL}
force_names = set(json.loads('''${FORCE_NAMES_JSON}'''))

svg_jobs, raster_jobs = [], []
for name, entry in manifest.items():
    is_svg = entry['source_path'].lower().endswith('.svg')
    out = ('${VECTOR_DIR}/' if is_svg else '${RASTER_DIR}/') + name + ('.svg' if is_svg else '.png')
    needs = force_all or name in force_names or not os.path.exists(out)
    if not needs:
        continue
    job = {'name': name, 'source_path': entry['source_path'], 'is_status': entry.get('is_status', False)}
    (svg_jobs if is_svg else raster_jobs).append(job)

json.dump(svg_jobs, open('${WORK}/svg_jobs.json', 'w'))
json.dump(raster_jobs, open('${WORK}/raster_jobs.json', 'w'))
print(f'>>> {len(svg_jobs)} vector job(s), {len(raster_jobs)} raster job(s) ({len(manifest)} total in manifest)')
"

SVG_COUNT=$(python3 -c "import json; print(len(json.load(open('${WORK}/svg_jobs.json'))))")
RASTER_COUNT=$(python3 -c "import json; print(len(json.load(open('${WORK}/raster_jobs.json'))))")

if [ "$SVG_COUNT" -eq 0 ] && [ "$RASTER_COUNT" -eq 0 ]; then
    echo ">>> Nothing to (re)generate."
    exit 0
fi

REMOTE_WORKDIR='$HOME/.config/timhyperland-icons'
REMOTE_ICON_PY="${REMOTE_WORKDIR}/icon_remote.py"

echo ">>> [1/4] Uploading helper scripts"
run "mkdir -p ${REMOTE_WORKDIR}"
run "cat > ${REMOTE_ICON_PY}" < ./lib/icon_remote.py

if [ "$RASTER_COUNT" -gt 0 ]; then
    REMOTE_RASTER_JOBS="${REMOTE_WORKDIR}/duotone-raster-jobs.json"
    REMOTE_RASTER_OUT="${REMOTE_WORKDIR}/duotone-raster-out"
    echo ">>> [2/4] Rasterizing ${RASTER_COUNT} PNG-sourced icon(s) to grayscale+alpha (size=${SIZE})"
    run "cat > ${REMOTE_RASTER_JOBS}" < "${WORK}/raster_jobs.json"
    run "rm -rf ${REMOTE_RASTER_OUT} && mkdir -p ${REMOTE_RASTER_OUT} && python3 ${REMOTE_ICON_PY} grayscale \
        --jobs ${REMOTE_RASTER_JOBS} --outdir ${REMOTE_RASTER_OUT} --size ${SIZE}"
    run "tar -C ${REMOTE_RASTER_OUT} -cf - . | base64 -w0" > "${WORK}/raster.tar.b64"
    base64 -d < "${WORK}/raster.tar.b64" | tar -C "$RASTER_DIR" -xf -
else
    echo ">>> [2/4] No PNG-sourced icons to rasterize"
fi

if [ "$SVG_COUNT" -gt 0 ]; then
    echo ">>> [3/4] Fetching ${SVG_COUNT} SVG source(s) from the target"
    # Enrich each job with its theme/relpath (from the manifest) so the
    # remote fetcher can prefer the BACKED-UP pristine original over
    # source_path -- source_path can itself point at a file our own prior
    # deploy already overwrote in place (e.g. kora, whose only copy is
    # user-local -- see 11-deploy-icon-theme.sh's header comment), so
    # trusting it blindly after a redeploy would re-ingest our own output.
    python3 -c "
import json
manifest = json.load(open('${MANIFEST}'))
jobs = json.load(open('${WORK}/svg_jobs.json'))
for j in jobs:
    entry = manifest.get(j['name'], {})
    j['source_theme'] = entry.get('source_theme')
    j['source_relpath'] = entry.get('source_relpath')
json.dump(jobs, open('${WORK}/svg_jobs_enriched.json', 'w'))
"
    REMOTE_SVG_JOBS="${REMOTE_WORKDIR}/duotone-svg-jobs.json"
    REMOTE_FETCH_PY="${REMOTE_WORKDIR}/fetch_sources.py"
    run "cat > ${REMOTE_SVG_JOBS}" < "${WORK}/svg_jobs_enriched.json"
    run "cat > ${REMOTE_FETCH_PY}" <<'FETCHPY'
import json, sys
from pathlib import Path

jobs = json.load(open(sys.argv[1]))
backup_root = Path.home() / ".config/timhyperland-icons/original-icons-backup"
out = {}
for j in jobs:
    candidates = []
    if j.get("source_theme") and j.get("source_relpath"):
        candidates.append(backup_root / j["source_theme"] / j["source_relpath"])
    candidates.append(Path(j["source_path"]))
    for p in candidates:
        if p.is_file():
            out[j["name"]] = p.read_text(errors="ignore")
            break
json.dump(out, sys.stdout)
FETCHPY
    run "python3 ${REMOTE_FETCH_PY} ${REMOTE_SVG_JOBS}" > "${WORK}/sources.json"
    FETCHED=$(python3 -c "import json; print(len(json.load(open('${WORK}/sources.json'))))")
    echo "    ${FETCHED} source(s) fetched"

    echo ">>> [4/4] Tagging vector masters by per-element lightness"
    python3 ./lib/icon_duotone_vector.py tag --sources "${WORK}/sources.json" --outdir "$VECTOR_DIR"
else
    echo ">>> [4/4] No SVG-sourced icons to vector-tag"
fi

echo ">>> [5/5] Finding every copy of each icon within its theme (kora ships the same"
echo "    icon under multiple contexts -- apps/devices/status/panel/actions/... --"
echo "    and overriding only the one we originally resolved left the others, e.g."
echo "    network-wired's devices/ copy, showing their original un-themed colors)"
python3 -c "
import json
manifest = json.load(open('${MANIFEST}'))
themed = {n: e['source_theme'] for n, e in manifest.items() if e.get('source_theme')}
json.dump(themed, open('${WORK}/themed_names.json', 'w'))
"
REMOTE_THEMED_NAMES="${REMOTE_WORKDIR}/themed-names.json"
REMOTE_ALIAS_PY="${REMOTE_WORKDIR}/find_aliases.py"
run "cat > ${REMOTE_THEMED_NAMES}" < "${WORK}/themed_names.json"
run "cat > ${REMOTE_ALIAS_PY}" <<'ALIASPY'
import json, sys
from pathlib import Path
from collections import defaultdict

themed = json.load(open(sys.argv[1]))  # {name: theme}
by_theme = defaultdict(set)
for name, theme in themed.items():
    by_theme[theme].add(name)

aliases = defaultdict(list)
for theme, names in by_theme.items():
    theme_dir = Path.home() / ".local/share/icons" / theme
    if not theme_dir.is_dir():
        continue
    for ext in ("svg", "png"):
        for p in theme_dir.rglob(f"*.{ext}"):
            if p.stem in names:
                aliases[p.stem].append(str(p.relative_to(theme_dir)))

json.dump(aliases, sys.stdout)
ALIASPY
run "python3 ${REMOTE_ALIAS_PY} ${REMOTE_THEMED_NAMES}" > "${WORK}/aliases.json"

python3 -c "
import json
manifest = json.load(open('${MANIFEST}'))
aliases = json.load(open('${WORK}/aliases.json'))
updated = 0
for name, relpaths in aliases.items():
    if name in manifest and len(relpaths) > 1:
        manifest[name]['alias_relpaths'] = sorted(relpaths)
        updated += 1
json.dump(manifest, open('${MANIFEST}', 'w'), indent=2, sort_keys=True)
print(f'>>> {updated} icon(s) have more than one copy within their theme; recorded for full override')
"

echo ">>> Done. Run ./15-deploy-duotone-theme.sh to stamp the current accent and install."
