"""Create the application PNG and multi-resolution Windows ICO."""

from __future__ import annotations

from pathlib import Path
import struct

from PySide6.QtCore import QByteArray, QBuffer, QIODevice, QRectF, Qt
from PySide6.QtGui import QImage, QPainter, QPainterPath


PROJECT_ROOT = Path(__file__).resolve().parents[1]
ASSET_DIRECTORY = PROJECT_ROOT / "assets"
SOURCE_PATH = ASSET_DIRECTORY / "musicgridstudio_icon_source.png"
PNG_PATH = ASSET_DIRECTORY / "musicgridstudio_icon.png"
ICO_PATH = ASSET_DIRECTORY / "musicgridstudio.ico"
ICON_SIZES = (16, 20, 24, 32, 40, 48, 64, 96, 128, 256)


def _prepared_icon() -> QImage:
    source = QImage(str(SOURCE_PATH))
    if source.isNull():
        raise RuntimeError(f"Could not load icon source: {SOURCE_PATH}")
    side = min(source.width(), source.height())
    source = source.copy(
        (source.width() - side) // 2,
        (source.height() - side) // 2,
        side,
        side,
    )
    source = source.scaled(
        1024,
        1024,
        Qt.AspectRatioMode.IgnoreAspectRatio,
        Qt.TransformationMode.SmoothTransformation,
    )

    result = QImage(
        1024,
        1024,
        QImage.Format.Format_ARGB32_Premultiplied,
    )
    result.fill(Qt.GlobalColor.transparent)
    painter = QPainter(result)
    painter.setRenderHints(
        QPainter.RenderHint.Antialiasing
        | QPainter.RenderHint.SmoothPixmapTransform
    )
    clip = QPainterPath()
    clip.addRoundedRect(QRectF(0, 0, 1024, 1024), 174, 174)
    painter.setClipPath(clip)
    painter.drawImage(0, 0, source)
    painter.end()
    return result


def _png_bytes(image: QImage) -> bytes:
    encoded = QByteArray()
    buffer = QBuffer(encoded)
    if not buffer.open(QIODevice.OpenModeFlag.WriteOnly):
        raise RuntimeError("Could not open the in-memory icon buffer.")
    if not image.save(buffer, "PNG"):
        raise RuntimeError("Could not encode an icon layer as PNG.")
    buffer.close()
    return bytes(encoded)


def _write_ico(image: QImage) -> None:
    layers: list[tuple[int, bytes]] = []
    for size in ICON_SIZES:
        scaled = image.scaled(
            size,
            size,
            Qt.AspectRatioMode.IgnoreAspectRatio,
            Qt.TransformationMode.SmoothTransformation,
        )
        layers.append((size, _png_bytes(scaled)))

    header_size = 6 + 16 * len(layers)
    offset = header_size
    entries: list[bytes] = []
    payloads: list[bytes] = []
    for size, payload in layers:
        dimension = 0 if size == 256 else size
        entries.append(
            struct.pack(
                "<BBBBHHII",
                dimension,
                dimension,
                0,
                0,
                1,
                32,
                len(payload),
                offset,
            )
        )
        payloads.append(payload)
        offset += len(payload)

    ICO_PATH.write_bytes(
        struct.pack("<HHH", 0, 1, len(layers))
        + b"".join(entries)
        + b"".join(payloads)
    )


def main() -> None:
    ASSET_DIRECTORY.mkdir(parents=True, exist_ok=True)
    image = _prepared_icon()
    if not image.save(str(PNG_PATH), "PNG"):
        raise RuntimeError(f"Could not save application icon: {PNG_PATH}")
    _write_ico(image)
    print(f"Created: {PNG_PATH}")
    print(f"Created: {ICO_PATH}")


if __name__ == "__main__":
    main()
