#!/usr/bin/env python3
# regenerate the .ICO file from .PSD.
import argparse
import tempfile
from dataclasses import dataclass
from pathlib import Path
from subprocess import check_call


@dataclass
class IconSpec:
    size: int
    type: str


SPECS = [
    IconSpec(size=32, type="bmp"),
    IconSpec(size=16, type="bmp"),
    IconSpec(size=256, type="png"),
    IconSpec(size=128, type="png"),
    IconSpec(size=64, type="png"),
    IconSpec(size=48, type="png"),
    IconSpec(size=32, type="png"),
    IconSpec(size=16, type="png"),
]


def parse_args() -> argparse.Namespace:
    parser = argparse.ArgumentParser()
    parser.add_argument("path", type=Path)
    parser.add_argument("-o", "--output", type=Path, required=True)
    return parser.parse_args()


def resize_transformer(path: Path, spec: IconSpec) -> None:
    check_call(
        [
            "convert",
            f"{path}[0]",
            "-filter",
            "lanczos",
            "-resize",
            f"{spec.size}x{spec.size}",
            f'PNG:{path}',
        ]
    )


def quantize_transformer(path: Path, spec: IconSpec) -> None:
    quantized_path = path.with_stem(f"{path.stem}-quantized")
    check_call(["pngquant", path, "--output", quantized_path])
    path.write_bytes(quantized_path.read_bytes())
    quantized_path.unlink()


def optimize_transformer(path: Path, spec: IconSpec) -> None:
    check_call(["zopflipng", "-y", path, path])


def convert_transformer(path: Path, spec: IconSpec) -> None:
    if spec.type != "png":
        check_call(["convert", path, f'{spec.type.upper()}:{path}'])


TRANSFORMERS = [
    resize_transformer,
    quantize_transformer,
    optimize_transformer,
    convert_transformer,
]


def generate_icon(source_path: Path, target_path: Path) -> None:
    aux_paths = []

    with tempfile.TemporaryDirectory() as tmpdir:
        tmp_path = Path(tmpdir)
        for spec in SPECS:
            aux_path = tmp_path / f"{spec.size}-{spec.type}.tmp"
            aux_path.write_bytes(source_path.read_bytes())
            for transform in TRANSFORMERS:
                transform(aux_path, spec)

            aux_paths.append(aux_path)

        # NOTE: image order is important for certain software.
        check_call(["identify", *aux_paths])
        check_call(["convert", *aux_paths, target_path])


def main() -> None:
    args = parse_args()

    if args.output.exists():
        args.output.unlink()
    generate_icon(args.path, args.output)


if __name__ == "__main__":
    main()
