from pathlib import Path
from collections import deque

from PIL import Image


ROOT = Path(__file__).resolve().parent
OUTPUT = ROOT.parent / "assets" / "kubejs" / "textures" / "item"
OUTPUT.mkdir(parents=True, exist_ok=True)


def largest_component_bbox(alpha: Image.Image) -> tuple[int, int, int, int]:
    width, height = alpha.size
    pixels = alpha.load()
    visited = bytearray(width * height)
    largest = []

    for y in range(height):
        for x in range(width):
            index = y * width + x
            if visited[index] or pixels[x, y] < 128:
                continue
            visited[index] = 1
            queue = deque([(x, y)])
            component = []
            while queue:
                px, py = queue.popleft()
                component.append((px, py))
                for nx, ny in ((px - 1, py), (px + 1, py), (px, py - 1), (px, py + 1)):
                    if 0 <= nx < width and 0 <= ny < height:
                        neighbor = ny * width + nx
                        if not visited[neighbor] and pixels[nx, ny] >= 128:
                            visited[neighbor] = 1
                            queue.append((nx, ny))
            if len(component) > len(largest):
                largest = component

    if not largest:
        raise RuntimeError("source has no visible connected component")
    xs = [point[0] for point in largest]
    ys = [point[1] for point in largest]
    return min(xs), min(ys), max(xs) + 1, max(ys) + 1


def downscale(name: str, subject_size: int) -> None:
    source = Image.open(ROOT / f"{name}_alpha.png").convert("RGBA")
    bbox = largest_component_bbox(source.getchannel("A"))

    cropped = source.crop(bbox)
    scale = min(subject_size / cropped.width, subject_size / cropped.height)
    size = (
        max(1, round(cropped.width * scale)),
        max(1, round(cropped.height * scale)),
    )
    sprite = cropped.resize(size, Image.Resampling.NEAREST)

    hard_alpha = sprite.getchannel("A").point(lambda value: 255 if value >= 128 else 0)
    sprite = sprite.convert("RGB").quantize(
        colors=12,
        method=Image.Quantize.MEDIANCUT,
        dither=Image.Dither.NONE,
    ).convert("RGBA")
    sprite.putalpha(hard_alpha)

    canvas = Image.new("RGBA", (16, 16), (0, 0, 0, 0))
    canvas.alpha_composite(sprite, ((16 - size[0]) // 2, (16 - size[1]) // 2))
    canvas.save(OUTPUT / f"{name}.png", optimize=False)


downscale("crimson_catalyst_base", 11)
downscale("crimson_catalyst", 14)
