#!/usr/bin/env bash
# ===============================================================================
# ImageMagick Utility Toolkit (magick_tools.sh)
# -------------------------------------------------------------------------------
# Author  : Alessandro Maestri
# Version : 2.2 (bash port)
# Date    : 2025-10-31
# -------------------------------------------------------------------------------
# Description:
#   Bash port of magick_tools.ps1 providing the same actions:
#     identify, convert, resize, optimize, make-ico, extract
#   Supports options:
#     --action|--input|--output|--width|--height|--sizes|--magick|--verbose
#     --prefix|--format|--colors|--no-dither
#   Features:
#     - Auto-detect ImageMagick (prefers 'magick', falls back to 'convert').
#     - Detect ImageMagick 7 vs legacy.
#     - Preserve alpha when building ICO (IM7 one-shot pipeline).
#     - Optional palette quantization for ICO (--colors, --no-dither).
#     - Extract all layers of .ico to numbered images.
#     - Supports --key=value or --key value forms.
# ===============================================================================
set -euo pipefail
IFS=$'\n\t'

# ----------------------------- DEFAULTS ---------------------------------
VALID_ACTIONS=(identify convert resize optimize make-ico extract)
PREFIX="layer"
FORMAT="png"
VERBOSE=0
MAGICK=""  # resolved later
ACTION=""
INPUT=""
OUTPUT=""
WIDTH=""
HEIGHT=""
SIZES=""
COLORS=""
NODITHER=0

# ----------------------------- UTILITIES --------------------------------
log_info() { printf '%s\n' "$*"; }
log_warn() { printf 'WARNING: %s\n' "$*" >&2; }
log_error() { printf 'ERROR: %s\n' "$*" >&2; }
verbose() { if [[ $VERBOSE -eq 1 ]]; then printf '[VERBOSE] %s\n' "$*"; fi }

usage() {
  cat <<EOF
Usage:
  magick_tools.sh --action identify --input assets/rbackup.ico
  magick_tools.sh --action convert --input logo.png --output logo.webp
  magick_tools.sh --action resize --input img.png --output img_256.png --width 256
  magick_tools.sh --action make-ico --input logo.png --output favicon.ico --sizes 16,32,48
  magick_tools.sh --action extract --input favicon.ico --output layers --prefix icon --format webp
Options:
  --action        (required) one of: identify, convert, resize, optimize, make-ico, extract
  --input         input file
  --output        output file (file for convert/resize/optimize/make-ico; directory for extract)
  --width         width for resize
  --height        optional height for resize
  --sizes         comma-separated sizes for make-ico (e.g. 16,32,48)
  --magick        path to magick or convert binary (auto-detect if omitted)
  --verbose       enable verbose logging
  --prefix        filename prefix for extract (default: layer)
  --format        output format for extract (png|jpg|webp) default: png
  --colors        color palette size for make-ico (omit to keep truecolor + alpha)
  --no-dither     disable dithering when --colors is used
Examples:
  magick_tools.sh --action make-ico --input logo.png --output favicon.ico --sizes 16,32,48,64 --colors 256 --no-dither
  magick_tools.sh --action extract --input favicon.ico --output layers --prefix icon --format webp --verbose
EOF
}

contains() { # needle in list
  local n=$1; shift; local e; for e in "$@"; do [[ $e == "$n" ]] && return 0; done; return 1;
}

resolve_path() {
  local p=$1
  if [[ -z $p ]]; then echo ""; return; fi
  if [[ $p == /* ]]; then echo "$p"; else
    # Try realpath; fallback to manual
    if command -v realpath >/dev/null 2>&1; then realpath "$p"; else echo "$(pwd)/$p"; fi
  fi
}

# ----------------------------- ARG PARSER -------------------------------
if [[ $# -eq 0 ]]; then usage; exit 1; fi

while (( "$#" )); do
  case "$1" in
    --action|--input|--output|--width|--height|--sizes|--magick|--prefix|--format|--colors)
      key="${1#--}"; val="${2-}"; if [[ -z $val || $val == -* ]]; then log_error "Missing value for $1"; usage; exit 1; fi; eval "${key^^}='$val'"; shift 2;;
    --action=*)   ACTION="${1#*=}"; shift;;
    --input=*)    INPUT="${1#*=}"; shift;;
    --output=*)   OUTPUT="${1#*=}"; shift;;
    --width=*)    WIDTH="${1#*=}"; shift;;
    --height=*)   HEIGHT="${1#*=}"; shift;;
    --sizes=*)    SIZES="${1#*=}"; shift;;
    --magick=*)   MAGICK="${1#*=}"; shift;;
    --prefix=*)   PREFIX="${1#*=}"; shift;;
    --format=*)   FORMAT="${1#*=}"; shift;;
    --colors=*)   COLORS="${1#*=}"; shift;;
    --verbose)    VERBOSE=1; shift;;
    --no-dither)  NODITHER=1; shift;;
    --help|-h)    usage; exit 0;;
    --*)          log_warn "Unknown option '$1' ignored"; shift;;
    *)            log_error "Unexpected bare value '$1'"; usage; exit 1;;
  esac
done

# ----------------------------- VALIDATION ------------------------------
if [[ -z $ACTION ]]; then log_error "Missing --action"; usage; exit 1; fi
if ! contains "$ACTION" "${VALID_ACTIONS[@]}"; then log_error "Invalid action '$ACTION'"; usage; exit 1; fi

INPUT_RESOLVED="$(resolve_path "$INPUT")"
OUTPUT_RESOLVED="$(resolve_path "$OUTPUT")"

if [[ -n $INPUT && ! -f $INPUT_RESOLVED ]]; then log_error "Input file not found: $INPUT (resolved: $INPUT_RESOLVED)"; exit 1; fi

# Auto-detect ImageMagick binary (robusto: evita convert.exe di Windows system32)
if [[ -z $MAGICK ]]; then
  detect_candidates=(magick magick.exe)
  for c in "${detect_candidates[@]}"; do
    if command -v "$c" >/dev/null 2>&1; then
      if "$c" -version 2>/dev/null | grep -qi 'ImageMagick'; then MAGICK="$(command -v "$c")"; break; fi
    fi
  done
  if [[ -z $MAGICK ]]; then
    # Try convert but exclude system32 convert (disk format tool)
    if command -v convert >/dev/null 2>&1; then
      conv_path="$(command -v convert)"
      if [[ "$conv_path" =~ (system32/convert.exe|SYSTEM32/convert.exe) ]]; then
        log_warn "Ignoring system Windows convert at $conv_path (not ImageMagick)."
      else
        if convert -version 2>/dev/null | grep -qi 'ImageMagick'; then MAGICK="$conv_path"; fi
      fi
    fi
  fi
  if [[ -z $MAGICK ]]; then
    log_error "ImageMagick not found. Install ImageMagick 7 (magick) or provide --magick /path/to/magick.exe"; exit 1
  fi
else
  if [[ ! -x $MAGICK ]]; then log_error "Specified --magick not executable: $MAGICK"; exit 1; fi
  if ! "$MAGICK" -version 2>/dev/null | grep -qi 'ImageMagick'; then
    log_error "Provided --magick does not appear to be ImageMagick: $MAGICK"; exit 1
  fi
fi

# Detect version (IM7 uses 'magick'); ensure we are not using system convert
MAGICK_VERSION_LINE="$($MAGICK -version 2>/dev/null | head -n1 || true)"
IS_MAGICK7=0
if [[ $MAGICK_VERSION_LINE =~ ImageMagick[[:space:]]+7 ]]; then IS_MAGICK7=1; fi

# Validate format for extract
FORMAT_LOWER="${FORMAT,,}"; [[ $FORMAT_LOWER == "jpeg" ]] && FORMAT_LOWER="jpg"
if [[ $ACTION == "extract" ]]; then
  case "$FORMAT_LOWER" in png|jpg|webp) ;; *) log_error "Unsupported --format '$FORMAT' (allowed: png, jpg, webp)"; exit 1;; esac
fi

# Validate colors numeric
if [[ -n $COLORS && ! $COLORS =~ ^[0-9]+$ ]]; then log_error "--colors must be numeric"; exit 1; fi

# ----------------------------- HEADER ----------------------------------
start_ts="$(date '+%Y-%m-%d %H:%M:%S')"
log_info "=== ImageMagick Toolkit (bash) ==="
log_info "Action : $ACTION"
log_info "Input  : ${INPUT:-}"; log_info "         resolved -> ${INPUT_RESOLVED:-N/A}"
log_info "Output : ${OUTPUT:-}"; log_info "         resolved -> ${OUTPUT_RESOLVED:-N/A}"
log_info "Magick : $MAGICK (IM7=$IS_MAGICK7)"
log_info "Verbose: $VERBOSE"
log_info "Prefix : $PREFIX"
log_info "Format : $FORMAT_LOWER"
log_info "Colors : ${COLORS:-}"
log_info "NoDith : $NODITHER"
log_info "Started: $start_ts"
log_info "----------------------------------"

# ----------------------------- FUNCTIONS -------------------------------
build_ico_im7() {
  local in=$1 out=$2 sizes_csv=$3 has_alpha=$4
  local args=("$in" -background none)
  if [[ $has_alpha -eq 1 ]]; then args+=( -alpha set ); else args+=( -alpha off ); fi
  args+=( -define "icon:auto-resize=$sizes_csv" -depth 32 )
  if [[ -n $COLORS ]]; then
    args+=( -colors "$COLORS" )
    [[ $NODITHER -eq 1 ]] && args+=( -dither None )
  fi
  args+=( "$out" )
  verbose "IM7 ICO build: $MAGICK ${args[*]}"
  "$MAGICK" "${args[@]}"
}

build_ico_legacy() {
  local in=$1 out=$2 sizes_csv=$3 has_alpha=$4
  local tmp_files=()
  IFS=',' read -r -a sizes_arr <<<"$sizes_csv"
  for s in "${sizes_arr[@]}"; do
    local tmp
    tmp="$(mktemp --suffix ".png")"
    local args=("$in" -background none)
    [[ $has_alpha -eq 1 ]] && args+=( -alpha set )
    args+=( -resize "${s}x${s}" "PNG32:$tmp")
    verbose "Legacy gen ${s}x${s}: $MAGICK ${args[*]}"
    "$MAGICK" "${args[@]}"
    tmp_files+=("$tmp")
  done
  local assemble=("${tmp_files[@]}")
  if [[ -n $COLORS ]]; then
    assemble+=( -colors "$COLORS" )
    [[ $NODITHER -eq 1 ]] && assemble+=( -dither None )
  fi
  assemble+=( "$out" )
  verbose "Legacy assemble: $MAGICK convert ${assemble[*]}"
  if [[ $IS_MAGICK7 -eq 1 ]]; then "$MAGICK" "${assemble[@]}"; else "$MAGICK" convert "${assemble[@]}"; fi
  for f in "${tmp_files[@]}"; do rm -f "$f" || true; done
}

extract_layers() {
  local in=$1 out_dir=$2 prefix=$3 fmt=$4
  mkdir -p "$out_dir"
  local count
  count="$($MAGICK identify -format "%n" "$in" 2>/dev/null || true)"
  if [[ -z $count || $count -le 0 ]]; then log_error "No frames detected in icon: $in"; exit 1; fi
  verbose "Extract frame count: $count"
  for ((i=0;i<count;i++)); do
    local out_file="$out_dir/${prefix}_${i}.${fmt}"
    if [[ $IS_MAGICK7 -eq 1 ]]; then "$MAGICK" "${in[$i]}" "$out_file"; else "$MAGICK" convert "${in[$i]}" "$out_file"; fi
    verbose "Extracted layer $i -> $out_file"
  done
  log_info "✅ Extracted $count layer(s) to: $out_dir (format: $fmt prefix: $prefix)"
}

# ----------------------------- DISPATCH --------------------------------
case "$ACTION" in
  identify)
    [[ -z $INPUT ]] && { log_error "Missing --input"; exit 1; }
    verbose "Identify channels..."
    "$MAGICK" identify "$INPUT_RESOLVED" || { log_error "Identify failed"; exit 1; }
    ;;
  convert)
    [[ -z $INPUT || -z $OUTPUT ]] && { log_error "Missing --input or --output"; exit 1; }
    "$MAGICK" "$INPUT_RESOLVED" "$OUTPUT_RESOLVED" || { log_error "Convert failed"; exit 1; }
    log_info "✅ Converted: $INPUT_RESOLVED -> $OUTPUT_RESOLVED"
    ;;
  resize)
    [[ -z $INPUT || -z $OUTPUT || -z $WIDTH ]] && { log_error "Missing --input --output --width"; exit 1; }
    size="$WIDTH"; [[ -n $HEIGHT ]] && size="${WIDTH}x${HEIGHT}"
    if [[ $IS_MAGICK7 -eq 1 ]]; then "$MAGICK" "$INPUT_RESOLVED" -resize "$size" "$OUTPUT_RESOLVED"; else "$MAGICK" convert "$INPUT_RESOLVED" -resize "$size" "$OUTPUT_RESOLVED"; fi
    log_info "✅ Resized $INPUT_RESOLVED -> $OUTPUT_RESOLVED ($size)"
    ;;
  optimize)
    [[ -z $INPUT ]] && { log_error "Missing --input"; exit 1; }
    out="${OUTPUT_RESOLVED:-$INPUT_RESOLVED}"
    if [[ $IS_MAGICK7 -eq 1 ]]; then "$MAGICK" "$INPUT_RESOLVED" -strip -interlace Plane -quality 85% "$out"; else "$MAGICK" convert "$INPUT_RESOLVED" -strip -interlace Plane -quality 85% "$out"; fi
    log_info "✅ Optimized $INPUT_RESOLVED -> $out"
    ;;
  make-ico)
    [[ -z $INPUT || -z $OUTPUT || -z $SIZES ]] && { log_error "Missing --input --output --sizes"; exit 1; }
    channel_info="$($MAGICK identify -format "%[channels]" "$INPUT_RESOLVED" 2>/dev/null || true)"
    has_alpha=0; [[ $channel_info =~ a|alpha ]] && has_alpha=1
    verbose "Channels: $channel_info alpha=$has_alpha"
    sizes_csv="$SIZES"
    if [[ $IS_MAGICK7 -eq 1 ]]; then
      build_ico_im7 "$INPUT_RESOLVED" "$OUTPUT_RESOLVED" "$sizes_csv" "$has_alpha"
    else
      build_ico_legacy "$INPUT_RESOLVED" "$OUTPUT_RESOLVED" "$sizes_csv" "$has_alpha"
    fi
    log_info "✅ ICO created: $OUTPUT_RESOLVED (sizes: $sizes_csv alpha=$has_alpha colors=${COLORS:-})"
    ;;
  extract)
    [[ -z $INPUT ]] && { log_error "Missing --input"; exit 1; }
    out_dir="${OUTPUT_RESOLVED:-$(pwd)/layers}"; extract_layers "$INPUT_RESOLVED" "$out_dir" "$PREFIX" "$FORMAT_LOWER"
    ;;
  *) log_error "Unknown action '$ACTION'"; usage; exit 1;;
esac

end_ms=$(date +%s%3N 2>/dev/null || date +%s)
verbose "Elapsed ms: $end_ms"
log_info "🎉 Operation completed successfully."
exit 0
