#!/usr/bin/env python3
"""Generate spacecraft spritesheet PNG for the JNR engine.

Output: assets/sprites/spacecraft.png
Layout: 5 frames horizontally, each 80x48 pixels = 400x48 total
Frames: landed, landing1, landing2, landing3, landing4

The ship faces RIGHT, uses the game's pixel art color palette.
"""

from PIL import Image

# ── Color palette (RGBA) ─────────────────────────────
T   = (0, 0, 0, 0)         # transparent
BLK = (26, 26, 46, 255)    # dark/outline
WHT = (238, 238, 234, 255) # white
GRY = (136, 136, 136, 255) # grey
GYD = (85, 85, 85, 255)    # grey dark
GYL = (187, 187, 187, 255) # grey light
BLU = (74, 124, 189, 255)  # blue accent
BLD = (54, 94, 143, 255)   # blue dark
BLL = (111, 168, 220, 255) # blue light
CYN = (77, 208, 225, 255)  # cyan cockpit
CYD = (0, 172, 193, 255)   # cyan dark
ORG = (255, 152, 0, 255)   # orange
ORD = (204, 122, 0, 255)   # orange dark
YLW = (255, 213, 79, 255)  # yellow
RED = (217, 68, 68, 255)   # red

W, H = 80, 48  # frame size


def make_frame():
    """Return a blank 80x48 pixel grid."""
    return [[T] * W for _ in range(H)]


def hline(grid, y, x0, x1, color):
    """Draw horizontal line from x0 to x1 inclusive."""
    if y < 0 or y >= H:
        return
    for x in range(max(0, x0), min(W, x1 + 1)):
        grid[y][x] = color


def pixel(grid, x, y, color):
    """Set a single pixel."""
    if 0 <= x < W and 0 <= y < H:
        grid[y][x] = color


def draw_ship_body(grid, oy=0):
    """Draw the main spacecraft hull, offset vertically by oy pixels.

    Ship anatomy (facing right):
    - Engine block: cols 10-18, thick rear
    - Main fuselage: cols 18-58, ~10px tall
    - Cockpit: cols 58-66, with CYN canopy
    - Nose: cols 66-72, pointed tip
    - Upper fin: cols 22-38, extends upward
    - Lower fin: cols 22-38, extends downward
    """
    cy = 23 + oy  # vertical center of fuselage

    # ── Main fuselage (rows cy-5 to cy+4 = 10px tall) ──
    for dy in range(-5, 5):
        y = cy + dy
        # Fuselage hull
        if dy == -5:
            # Top edge — light highlight
            hline(grid, y, 20, 60, GYL)
        elif dy == 4:
            # Bottom edge — dark shadow
            hline(grid, y, 20, 60, BLK)
        elif dy == -4:
            hline(grid, y, 18, 62, GYL)
        elif dy == 3:
            hline(grid, y, 18, 62, GYD)
        elif dy == -1 or dy == 0:
            # Blue racing stripe through center
            hline(grid, y, 16, 64, BLU)
        elif dy == -2:
            hline(grid, y, 17, 63, GRY)
        elif dy == 1:
            hline(grid, y, 17, 63, GRY)
        elif dy == 2:
            hline(grid, y, 18, 62, GYD)
        elif dy == -3:
            hline(grid, y, 19, 61, GRY)

    # ── Engine block (rear, left side) ──
    for dy in range(-6, 7):
        y = cy + dy
        if -6 <= dy <= 6:
            # Engine nacelle housing
            if abs(dy) <= 3:
                hline(grid, y, 10, 18, GYD)
            elif abs(dy) <= 5:
                hline(grid, y, 12, 18, GYD)
            elif abs(dy) == 6:
                hline(grid, y, 14, 17, GYD)

    # Engine inner dark (thruster openings)
    for dy in range(-3, 4):
        y = cy + dy
        if abs(dy) <= 2:
            hline(grid, y, 10, 12, BLK)
        elif abs(dy) == 3:
            pixel(grid, 11, y, BLK)
            pixel(grid, 12, y, BLK)

    # Engine highlight on top
    hline(grid, cy - 4, 13, 17, GRY)
    hline(grid, cy - 5, 14, 17, GRY)

    # ── Cockpit canopy ──
    # Canopy frame (CYD) and glass (CYN)
    for dy in range(-4, 3):
        y = cy + dy
        if dy == -4:
            hline(grid, y, 58, 63, CYD)
        elif dy == -3:
            hline(grid, y, 59, 65, CYD)
            hline(grid, y, 60, 64, CYN)
        elif dy == -2:
            hline(grid, y, 60, 66, CYD)
            hline(grid, y, 61, 65, CYN)
            pixel(grid, 63, y, WHT)  # glint
        elif dy == -1:
            hline(grid, y, 61, 67, CYD)
            hline(grid, y, 62, 66, CYN)
        elif dy == 0:
            hline(grid, y, 61, 67, CYD)
            hline(grid, y, 62, 66, CYN)
            pixel(grid, 64, y, WHT)  # glint
        elif dy == 1:
            hline(grid, y, 60, 66, CYD)
            hline(grid, y, 61, 65, CYN)
        elif dy == 2:
            hline(grid, y, 58, 64, CYD)

    # ── Nose (pointed tip facing right) ──
    for dy in range(-3, 4):
        y = cy + dy
        if dy == 0:
            hline(grid, y, 66, 73, GYL)  # center line
        elif abs(dy) == 1:
            hline(grid, y, 66, 71, GRY)
        elif abs(dy) == 2:
            hline(grid, y, 65, 69, GYD)
        elif abs(dy) == 3:
            hline(grid, y, 65, 67, BLK)

    # Nose tip highlight
    pixel(grid, 73, cy, WHT)
    pixel(grid, 72, cy, WHT)

    # ── Upper wing fin ──
    for i in range(8):
        y = cy - 6 - i
        x0 = 26 + i
        x1 = 40 - i
        if x0 <= x1:
            hline(grid, y, x0, x1, GYD)
            # Highlight top edge
            if i < 7:
                pixel(grid, x0, y, GYL)
            # BLU accent
            if x1 - x0 > 2:
                pixel(grid, (x0 + x1) // 2, y, BLU)

    # Wing fin tip accent
    pixel(grid, 33, cy - 13, BLU)

    # ── Lower wing fin ──
    for i in range(8):
        y = cy + 5 + i
        x0 = 26 + i
        x1 = 40 - i
        if x0 <= x1:
            hline(grid, y, x0, x1, GYD)
            # Shadow bottom edge
            if i < 7:
                pixel(grid, x0, y, BLK)
            # BLU accent
            if x1 - x0 > 2:
                pixel(grid, (x0 + x1) // 2, y, BLD)

    # ── Panel detail lines ──
    for dy in [-3, 2]:
        y = cy + dy
        for x in [30, 42, 52]:
            pixel(grid, x, y, GYD if dy < 0 else BLK)


def draw_landing_gear(grid, oy=0, length=4):
    """Draw two landing gear struts below the hull."""
    cy = 23 + oy
    gear_top = cy + 5  # just below hull bottom

    # Front gear (col 52)
    for dy in range(length):
        y = gear_top + dy
        pixel(grid, 52, y, GYD)
        pixel(grid, 53, y, GYD)
    # Foot
    hline(grid, gear_top + length, 51, 54, BLK)

    # Rear gear (col 26)
    for dy in range(length):
        y = gear_top + dy
        pixel(grid, 26, y, GYD)
        pixel(grid, 27, y, GYD)
    # Foot
    hline(grid, gear_top + length, 25, 28, BLK)


def draw_thrusters(grid, oy=0, intensity=0):
    """Draw thruster flames behind the engine.

    intensity: 0=off, 1=dim, 2=medium, 3=bright
    """
    if intensity == 0:
        return

    cy = 23 + oy

    if intensity >= 3:
        # Bright: long flame with white core
        for dy in range(-2, 3):
            y = cy + dy
            if abs(dy) == 0:
                hline(grid, y, 1, 9, YLW)
                hline(grid, y, 3, 7, WHT)
            elif abs(dy) == 1:
                hline(grid, y, 3, 9, ORG)
                hline(grid, y, 4, 8, YLW)
            elif abs(dy) == 2:
                hline(grid, y, 5, 9, ORD)
        # Flame tip flicker
        pixel(grid, 1, cy, WHT)
        pixel(grid, 2, cy, YLW)
    elif intensity >= 2:
        # Medium flame
        for dy in range(-1, 2):
            y = cy + dy
            if dy == 0:
                hline(grid, y, 4, 9, YLW)
                hline(grid, y, 5, 8, WHT)
            else:
                hline(grid, y, 5, 9, ORG)
                hline(grid, y, 6, 8, YLW)
        pixel(grid, 4, cy - 2, ORD)
        pixel(grid, 4, cy + 2, ORD)
    elif intensity >= 1:
        # Dim glow
        for dy in range(-1, 2):
            y = cy + dy
            if dy == 0:
                hline(grid, y, 7, 9, ORD)
                pixel(grid, 8, y, ORG)
            else:
                pixel(grid, 8, y, ORD)
                pixel(grid, 9, y, ORD)


def apply_tilt(grid, tilt_pixels):
    """Shift the left side of the image down by tilt_pixels to simulate
    nose-up tilt. This is a crude per-column vertical shift."""
    if tilt_pixels == 0:
        return grid

    new_grid = make_frame()
    for x in range(W):
        # Linear interpolation: left edge shifts down by tilt_pixels,
        # right edge stays the same
        shift = int(tilt_pixels * (1.0 - x / W))
        for y in range(H):
            src_y = y - shift
            if 0 <= src_y < H:
                new_grid[y][x] = grid[src_y][x]
    return new_grid


def make_landed():
    """Frame: ship landed on ground, gear down, no thrust."""
    grid = make_frame()
    draw_ship_body(grid, oy=0)
    draw_landing_gear(grid, oy=0, length=5)
    return grid


def make_landing1():
    """Frame 1: ship high, tilted nose-up, bright thrusters."""
    grid = make_frame()
    draw_ship_body(grid, oy=-8)
    draw_thrusters(grid, oy=-8, intensity=3)
    grid = apply_tilt(grid, 3)
    return grid


def make_landing2():
    """Frame 2: ship lower, less tilt, medium thrusters."""
    grid = make_frame()
    draw_ship_body(grid, oy=-4)
    draw_thrusters(grid, oy=-4, intensity=2)
    grid = apply_tilt(grid, 1)
    return grid


def make_landing3():
    """Frame 3: ship nearly level, near ground, gear extending, dim thrusters."""
    grid = make_frame()
    draw_ship_body(grid, oy=-2)
    draw_landing_gear(grid, oy=-2, length=3)
    draw_thrusters(grid, oy=-2, intensity=1)
    return grid


def make_landing4():
    """Frame 4: ship touching down, gear extended, no thrust."""
    grid = make_frame()
    draw_ship_body(grid, oy=0)
    draw_landing_gear(grid, oy=0, length=5)
    # Nearly identical to landed — maybe a very slight glow
    pixel(grid, 9, 23, ORD)  # residual engine heat
    return grid


def grid_to_image(grid):
    """Convert a pixel grid to a PIL Image."""
    img = Image.new("RGBA", (W, H), (0, 0, 0, 0))
    for y in range(H):
        for x in range(W):
            img.putpixel((x, y), grid[y][x])
    return img


def main():
    frames = [
        make_landed(),
        make_landing1(),
        make_landing2(),
        make_landing3(),
        make_landing4(),
    ]

    # Combine into horizontal spritesheet
    sheet = Image.new("RGBA", (W * len(frames), H), (0, 0, 0, 0))
    for i, frame in enumerate(frames):
        img = grid_to_image(frame)
        sheet.paste(img, (i * W, 0))

    out_path = "assets/sprites/spacecraft.png"
    sheet.save(out_path)
    print(f"Saved spritesheet: {out_path} ({sheet.width}x{sheet.height})")


if __name__ == "__main__":
    main()
