#!/usr/bin/env python3
"""
Generate 100 small BMP images: img001.bmp .. img100.bmp
- 16x16 pixels
- 8-bit indexed (palette) BMP (BI_RGB, uncompressed)
- Each image has a distinct pattern + embedded index color cue.

No external dependencies.
"""

import os
import struct
from pathlib import Path

OUT_DIR = Path(r"images/")  # change to your folder
WIDTH, HEIGHT = 16, 16
BPP = 8  # indexed 8-bit
NUM_IMAGES = 100

def bmp_bytes_indexed_8bit(width: int, height: int, pixels_topdown: bytes, palette_bgr0: bytes) -> bytes:
    """
    Build a minimal BMP (BITMAPFILEHEADER + BITMAPINFOHEADER + palette + pixel array).
    pixels_topdown: width*height bytes, row-major top-to-bottom.
    palette_bgr0: 256*4 bytes, each entry B,G,R,0.
    """
    assert len(pixels_topdown) == width * height
    assert len(palette_bgr0) == 256 * 4

    # BMP rows are stored bottom-up by default, and each row padded to 4-byte boundary.
    row_bytes = width  # 1 byte per pixel at 8bpp
    row_padded = (row_bytes + 3) & ~3
    padding = b"\x00" * (row_padded - row_bytes)

    # Convert top-down buffer to bottom-up with padding per row
    pixel_rows = []
    for y in range(height - 1, -1, -1):
        start = y * width
        pixel_rows.append(pixels_topdown[start:start + width] + padding)
    pixel_array = b"".join(pixel_rows)

    # Headers
    bfType = b"BM"
    bfOffBits = 14 + 40 + len(palette_bgr0)  # file header + info header + palette
    bfSize = bfOffBits + len(pixel_array)

    file_header = struct.pack("<2sIHHI", bfType, bfSize, 0, 0, bfOffBits)

    biSize = 40
    biWidth = width
    biHeight = height  # positive => bottom-up
    biPlanes = 1
    biBitCount = BPP
    biCompression = 0  # BI_RGB
    biSizeImage = len(pixel_array)
    biXPelsPerMeter = 2835  # ~72 DPI
    biYPelsPerMeter = 2835
    biClrUsed = 256
    biClrImportant = 0

    info_header = struct.pack(
        "<IIIHHIIIIII",
        biSize, biWidth, biHeight, biPlanes, biBitCount,
        biCompression, biSizeImage,
        biXPelsPerMeter, biYPelsPerMeter,
        biClrUsed, biClrImportant
    )

    return file_header + info_header + palette_bgr0 + pixel_array

def make_palette() -> bytes:
    """
    Create a simple 256-color palette (B,G,R,0).
    We'll do a grayscale ramp, but reserve a few indices for accent colors.
    """
    pal = bytearray()
    for i in range(256):
        # default grayscale
        r = g = b = i
        pal += bytes([b, g, r, 0])

    # Add a few vivid accents (indices near the end)
    accents = {
        250: (255, 0, 0),    # red
        251: (0, 255, 0),    # green
        252: (0, 0, 255),    # blue
        253: (255, 255, 0),  # yellow
        254: (255, 0, 255),  # magenta
        255: (0, 255, 255),  # cyan
    }
    for idx, (r, g, b) in accents.items():
        off = idx * 4
        pal[off:off+4] = bytes([b, g, r, 0])

    return bytes(pal)

def generate_pixels(i: int, width: int, height: int) -> bytes:
    """
    Create a distinct 16x16 pattern per index using simple math:
    - background is grayscale gradient
    - a diagonal + border use accent colors depending on i
    """
    accent_idx = 250 + (i % 6)  # 250..255

    buf = bytearray(width * height)
    for y in range(height):
        for x in range(width):
            # base grayscale pattern varies by i
            base = (x * 16 + y * 8 + i * 3) & 0xFF

            # border
            if x == 0 or y == 0 or x == width - 1 or y == height - 1:
                val = accent_idx
            # diagonal
            elif x == y or x == (width - 1 - y):
                val = accent_idx
            # small "marker" block encoding i
            elif 2 <= x <= 5 and 2 <= y <= 5:
                val = (i * 7 + x * 13 + y * 17) & 0xFF
            else:
                val = base

            buf[y * width + x] = val

    return bytes(buf)

def main():
    OUT_DIR.mkdir(parents=True, exist_ok=True)
    palette = make_palette()

    sizes = []
    for i in range(1, NUM_IMAGES + 1):
        pixels = generate_pixels(i, WIDTH, HEIGHT)
        bmp = bmp_bytes_indexed_8bit(WIDTH, HEIGHT, pixels, palette)
        fname = OUT_DIR / f"img{i:03d}.bmp"
        fname.write_bytes(bmp)
        sizes.append((fname.name, len(bmp)))

    # Print a small report
    min_s = min(s for _, s in sizes)
    max_s = max(s for _, s in sizes)
    print(f"Generated {NUM_IMAGES} BMPs in: {OUT_DIR}")
    print(f"Size range: {min_s} .. {max_s} bytes")
    print("First 5 sizes:", sizes[:5])

if __name__ == "__main__":
    main()