#!/usr/bin/env python3
"""
FreeExile Sprite & Texture Mipmap Chain Generator.
Computes full power-of-two downsampled mipmap chains (down to 1x1) for high-performance GPU sampling.
Prevents texture shimmering/aliasing and reduces memory cache misses during isometric camera zoom.
"""

from __future__ import annotations

import argparse
from pathlib import Path
from typing import List, Optional

from PIL import Image


def generate_mipmap_chain(
    image: Image.Image,
    min_dimension: int = 1,
) -> List[Image.Image]:
    """Generates downsampled mipmap levels down to min_dimension x min_dimension."""
    chain: List[Image.Image] = [image.copy()]
    curr = image

    while curr.width > min_dimension or curr.height > min_dimension:
        next_w = max(min_dimension, curr.width // 2)
        next_h = max(min_dimension, curr.height // 2)
        if next_w == curr.width and next_h == curr.height:
            break
        curr = curr.resize((next_w, next_h), Image.Resampling.LANCZOS)
        chain.append(curr)

    return chain


def save_mipmap_chain(
    image_path: Path | str,
    output_dir: Optional[Path | str] = None,
    min_dimension: int = 1,
) -> List[Path]:
    """Bakes and saves all mipmap levels to disk for the given texture."""
    src = Path(image_path).resolve()
    if not src.exists() or not src.is_file():
        raise FileNotFoundError(f"Source texture not found: {src}")

    target_dir = Path(output_dir).resolve() if output_dir else src.parent
    target_dir.mkdir(parents=True, exist_ok=True)

    with Image.open(src) as base_img:
        mode = "RGBA" if base_img.mode in ("RGBA", "LA") or ("transparency" in base_img.info) else "RGB"
        img = base_img.convert(mode)
        levels = generate_mipmap_chain(img, min_dimension=min_dimension)

    saved_paths: List[Path] = []
    for idx, mip_img in enumerate(levels):
        mip_path = target_dir / f"{src.stem}_mip{idx}{src.suffix}"
        mip_img.save(mip_path, format="PNG", optimize=True)
        saved_paths.append(mip_path)

    return saved_paths


def generate_and_save_mipmaps(
    atlas_path: Path | str,
    output_dir: Optional[Path | str] = None,
    min_dimension: int = 1,
) -> List[Path]:
    """Hook invoked by animation_pipeline.py to create mipmaps alongside atlas."""
    return save_mipmap_chain(atlas_path, output_dir, min_dimension=min_dimension)


def main() -> None:
    """CLI entry point for mipmap generation."""
    parser = argparse.ArgumentParser(description="FreeExile Texture Mipmap Generator")
    parser.add_argument("--input", "-i", type=Path, required=True, help="Input texture file")
    parser.add_argument("--output-dir", "-o", type=Path, default=None, help="Output directory")
    parser.add_argument("--min-dim", type=int, default=1, help="Minimum dimension (default: 1)")
    args = parser.parse_args()

    paths = save_mipmap_chain(args.input, args.output_dir, min_dimension=args.min_dim)
    print(f"Generated {len(paths)} mipmap levels for {args.input.name}:")
    for p in paths:
        print(f"  - {p.name} ({p.stat().st_size} bytes)")


if __name__ == "__main__":
    main()
