"""雾鸦岛·孤岛全景合成(docs/island-design-v3.md §6.1)。

地形:顶点等级 0=夜海 1=礁滩 2=荒草 3=泥径,40×30 格(32px/格 → 1280×960)。
物件:assets/sprites/objects/ravenisle/ 下的 PixelLab 素材,按设计方位摆放。
运行:python -m genesis.mapgen.island
"""

from __future__ import annotations

import logging
import math
from pathlib import Path

from PIL import Image

from genesis.mapgen.compose import (
    Placement,
    WangTileSheet,
    add_glow,
    grade_night,
    paste_objects,
    render_terrain,
    smooth_levels,
    suppress_warm,
)

logger = logging.getLogger(__name__)

ROOT = Path(__file__).resolve().parents[3]
TILESET_DIR = ROOT / "assets/sprites/tilesets/ravenisle"
OBJECT_DIR = ROOT / "assets/sprites/objects/ravenisle"
OUT_PATH = ROOT / "assets/sprites/maps/ravenisle_island.png"

CELLS_W, CELLS_H, TILE = 40, 30, 32  # 1280×960

# 渲染完成后回填 viz.json 的 region 锚点(像素坐标,场景中心)
REGIONS = {
    "前院": (640, 580),
    "码头": (640, 870),
    "船屋": (800, 810),
    "沙滩": (930, 760),
    "花园": (340, 540),
    "温室": (445, 600),
    "后山墓园": (280, 330),
    "荒废教堂": (185, 270),
    "断崖": (130, 130),
    "风暴林": (850, 320),
    "灯塔": (1055, 250),
}


def _fill_ellipse(levels: list[list[int]], cx: float, cy: float, rx: float, ry: float, value: int) -> None:
    """把椭圆内的顶点抬到至少 value(只升不降,便于叠加地块)。"""
    for vy in range(len(levels)):
        for vx in range(len(levels[0])):
            if ((vx - cx) / rx) ** 2 + ((vy - cy) / ry) ** 2 <= 1.0:
                levels[vy][vx] = max(levels[vy][vx], value)


def _draw_path(levels: list[list[int]], points: list[tuple[float, float]], value: int = 3) -> None:
    """沿折线把顶点设为泥径;只在已有草地(≥2)上铺,不越过礁滩与海。"""
    for (x0, y0), (x1, y1) in zip(points, points[1:]):
        steps = max(1, int(math.dist((x0, y0), (x1, y1)) * 2))
        for i in range(steps + 1):
            vx = round(x0 + (x1 - x0) * i / steps)
            vy = round(y0 + (y1 - y0) * i / steps)
            if 0 <= vy < len(levels) and 0 <= vx < len(levels[0]) and levels[vy][vx] >= 2:
                levels[vy][vx] = value


def build_levels() -> list[list[int]]:
    """全岛地形:主岛 + 西北断崖角 + 东北灯塔岬,内圈荒草,泥径串联各场景。"""
    levels = [[0] * (CELLS_W + 1) for _ in range(CELLS_H + 1)]

    # 礁滩岛体(等级 1)
    _fill_ellipse(levels, 20.5, 15, 15.5, 10.5, 1)   # 主岛
    _fill_ellipse(levels, 9, 8, 6.5, 5.5, 1)         # 西北断崖角
    _fill_ellipse(levels, 28, 10, 5.5, 4.5, 1)       # 通往灯塔的地颈
    _fill_ellipse(levels, 33, 7, 4.5, 3.5, 1)        # 灯塔岬(保持裸岩)
    _fill_ellipse(levels, 28, 23, 7, 4.5, 1)         # 东南灰沙滩外凸

    # 荒草内陆(等级 2)
    _fill_ellipse(levels, 20.5, 15, 13, 8.6, 2)
    _fill_ellipse(levels, 9, 8, 4.4, 3.6, 2)
    _fill_ellipse(levels, 28, 10, 4.2, 3.0, 2)  # 灯塔地颈铺草,让小径能通到岬角

    # 泥径(等级 3):前院枢纽 → 码头/花园→墓园/风暴林→灯塔方向
    hub = (20, 19)
    _draw_path(levels, [hub, (20, 25)])                       # 前院 → 码头
    _draw_path(levels, [hub, (15, 18), (11.5, 17)])           # 前院 → 花园
    _draw_path(levels, [(11.5, 17), (9, 12)])                 # 花园 → 墓园
    _draw_path(levels, [hub, (24, 16), (27, 12), (31, 9)])    # 前院 → 风暴林 → 灯塔方向

    return smooth_levels(levels)


# 物件清单:(文件名, 锚点x, 脚底y)——锚点为底边中心,按 y 排序自动遮挡
PLACEMENTS: list[tuple[str, int, int]] = [
    ("castle.png", 640, 480),        # 鸦堡(居中偏北高地)
    ("fountain.png", 640, 615),      # 前院干涸喷泉
    ("dolly_track.png", 548, 640),   # 前院摄影轨道残骸
    ("statue.png", 352, 545),        # 花园断臂雕像
    ("well.png", 300, 595),          # 花园古井
    ("greenhouse.png", 445, 635),    # 温室(花园东侧)
    ("graves.png", 285, 365),        # 后山墓园
    ("chapel.png", 185, 305),        # 荒废教堂
    ("lighthouse.png", 1055, 295),   # 灯塔(东北裸岩岬)
    ("pier.png", 640, 955),          # 码头栈桥(伸入夜海)
    ("boathouse.png", 798, 825),     # 船屋
    ("rowboat.png", 902, 790),       # 凿穿的小艇
    ("barrels.png", 952, 760),       # 半埋的空油桶
    # 风暴林(东北一片黑松)+ 断崖/墓园零星
    ("pine_big.png", 780, 330), ("pine_small.png", 845, 300),
    ("pine_big.png", 905, 355), ("pine_small.png", 955, 315),
    ("pine_big.png", 825, 390), ("pine_small.png", 745, 285),
    ("pine_big.png", 880, 280), ("pine_small.png", 935, 395),
    ("pine_small.png", 990, 345), ("pine_big.png", 755, 395),
    ("pine_small.png", 120, 200), ("pine_big.png", 175, 165),
    ("pine_small.png", 235, 285), ("pine_small.png", 90, 250),
]

# 灯光:鸦堡窗光(暖)、温室微光(冷白)
GLOWS = [
    (565, 380, 26, (255, 185, 80), 0.50),
    (700, 380, 26, (255, 185, 80), 0.50),
    (640, 440, 30, (255, 195, 95), 0.55),
    (445, 600, 22, (170, 195, 215), 0.30),
]


def compose_island() -> Image.Image:
    sheets = [
        WangTileSheet.from_files(TILESET_DIR / "ocean_ground_metadata.json", TILESET_DIR / "ocean_ground_image.png"),
        WangTileSheet.from_files(TILESET_DIR / "ground_grass_metadata.json", TILESET_DIR / "ground_grass_image.png"),
        WangTileSheet.from_files(TILESET_DIR / "grass_path_metadata.json", TILESET_DIR / "grass_path_image.png"),
    ]
    logger.info("渲染地形 %dx%d cells", CELLS_W, CELLS_H)
    canvas = suppress_warm(render_terrain(build_levels(), sheets))

    placements = []
    for name, x, y in PLACEMENTS:
        path = OBJECT_DIR / name
        if not path.exists():
            logger.warning("缺物件素材 %s,跳过", name)
            continue
        placements.append(Placement(Image.open(path).convert("RGBA"), x, y))
    paste_objects(canvas, placements)

    canvas = grade_night(canvas)
    for x, y, r, color, s in GLOWS:
        add_glow(canvas, x, y, r, color, s)
    return canvas


def main() -> None:
    logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(name)s %(message)s")
    img = compose_island()
    OUT_PATH.parent.mkdir(parents=True, exist_ok=True)
    img.save(OUT_PATH)
    logger.info("✅ 孤岛全景已合成 → %s (%dx%d)", OUT_PATH, *img.size)


if __name__ == "__main__":
    main()
