// gen_maps.js: Generate 10 Tiled-format JSON maps per PRD §10.
// Produces assets/maps/*.json
// Tile index reference (matches tools/gen_tileset.lua order):
// NOTE: Tiled uses global tile IDs where 0 = empty and localIndex N -> global N+1 (firstgid=1).
// We store raw LOCAL indices in T.* and convert to global IDs (+1, 0 stays 0=empty) on write.
const T = {
  grass: 0, path: 1, water: 2, flowersY: 3, flowersP: 4, flowersG: 5, berry: 6, tree: 7,
  birdTree: 8, bush: 9, wall: 10, wallTop: 11, roof: 12, roofEdge: 13, door: 14, window: 15,
  floor: 16, floorRug: 17, bed: 18, counter: 19, oven: 20, shelf: 21, shelfFull: 22, table: 23,
  chair: 24, stool: 25, well: 26, fog: 27, fogThick: 28, whiteWall: 29, whiteFloor: 30, whiteDoor: 31,
  whiteBed: 32, whiteMonitor: 33, whiteTable: 34, ribbon: 35, ribbon2: 36, picnic: 37, bench: 38, sign: 39,
  calendar: 40, paper: 41, chart: 42, fenceH: 43, fenceV: 44, lamp: 45, potPlant: 46, barrel: 47,
  crate: 48, grassAlt: 49, pathH: 50, pathV: 51, pathCross: 52, waterEdge: 53, grassDark: 54, pathCorner: 55,
  flowerMix: 56, berryEmpty: 57, fogBand: 58, whitePillar: 59, floorEdge: 60, wallTrim: 61, grassPath: 62, transparent: 63,
};
// EMPTY tile (global 0): use a sentinel; we map 63(transparent local) -> 0 empty on output, others +1.
const EMPTY = 63; // local index used for "transparent/empty"

const COLS = 8; // tileset columns

const fs = require('fs');
const path = require('path');
const outDir = 'assets/maps';
fs.mkdirSync(outDir, { recursive: true });

// Load the tileset descriptor to embed inline.
const tilesetJson = JSON.parse(fs.readFileSync('assets/tilesets/tileset.json', 'utf8'));

function makeMap(name, width, height, layers) {
  // layers: [{name, type:'tilelayer', data:number[] (len w*h)} or {name,type:'objectgroup', objects:[]}]
  // Find door objects and clear obstacle tiles at their positions so the player can walk through.
  const objLayer = layers.find(l => l.type === 'objectgroup');
  const doorTiles = new Set();
  if (objLayer) {
    for (const o of objLayer.objects) {
      if (o.type === 'door' || (o.name && o.name.startsWith('door'))) {
        const tx = Math.floor(o.x / 16), ty = Math.floor(o.y / 16);
        doorTiles.add(ty * width + tx);
      }
    }
  }
  const tiledLayers = layers.map(l => {
    if (l.type === 'tilelayer') {
      // convert local indices to global tile IDs: local N -> N+1, EMPTY(63) -> 0
      // also clear obstacle tiles at door positions
      const globalData = l.data.map((v, idx) => {
        if (l.name === 'obstacles' && doorTiles.has(idx)) return 0; // clear wall at door
        return (v === EMPTY ? 0 : v + 1);
      });
      return {
        name: l.name,
        type: 'tilelayer',
        x: 0, y: 0,
        width, height,
        visible: true,
        opacity: 1,
        offsetx: 0, offsety: 0,
        draworder: 'right-down',
        data: globalData,
      };
    } else {
      return {
        name: l.name,
        type: 'objectgroup',
        x: 0, y: 0,
        visible: true,
        opacity: 1,
        offsetx: 0, offsety: 0,
        draworder: 'index',
        objects: l.objects,
      };
    }
  });
  return {
    compressionlevel: -1,
    height, width,
    tilewidth: 16,
    tileheight: 16,
    type: 'map',
    orientation: 'orthogonal',
    renderorder: 'right-down',
    tiledversion: '1.12.2',
    // embed tileset inline so Phaser resolves it without external file
    tilesets: [{
      firstgid: 1,
      name: tilesetJson.name,
      columns: tilesetJson.columns,
      image: '../tilesets/tileset.png',
      imagewidth: tilesetJson.imagewidth,
      imageheight: tilesetJson.imageheight,
      margin: 0,
      spacing: 0,
      tilecount: tilesetJson.tilecount,
      tilewidth: 16,
      tileheight: 16,
      tiles: tilesetJson.tiles,
    }],
    layers: tiledLayers,
  };
}

// fill helpers
function fillGrid(w, h, val) { return new Array(w * h).fill(val); }
function set(g, w, x, y, v) { if (x>=0&&y>=0&&x<w&&y<g.length/w) g[y*w+x] = v; }
function rectFill(g, w, h, x0, y0, rw, rh, v) {
  for (let y=y0; y<y0+rh && y<h; y++) for (let x=x0; x<x0+rw && x<w; x++) set(g, w, x, y, v);
}
function borderFill(g, w, h, x0, y0, rw, rh, v) {
  for (let y=y0; y<y0+rh && y<h; y++) for (let x=x0; x<x0+rw && x<w; x++) {
    if (y===y0||y===y0+rh-1||x===x0||x===x0+rw-1) set(g,w,x,y,v);
  }
}

// object helpers (pixel coords: x,y = top-left in pixels, 16 per tile)
function obj(name, type, tx, ty, props={}) {
  return { name, type, x: tx*16, y: ty*16, width: props._w||16, height: props._h||16,
           properties: Object.entries(props).filter(([k])=>!k.startsWith('_')).map(([k,v])=>({name:k,type:'string',value:String(v)})) };
}

const maps = {};

// 1. House (12x8) interior
{
  const w=12, h=8;
  const ground = fillGrid(w,h,T.floor);
  rectFill(ground,w,h,0,0,12,1,T.wallTop);
  rectFill(ground,w,h,0,h-1,12,1,T.floorEdge);
  const obs = fillGrid(w,h,63); // transparent
  borderFill(obs,w,h,0,0,12,8,T.wall);
  // bed bottom-left
  rectFill(obs,w,h,1,5,3,2,T.bed);
  // calendar on wall (C1) - object only
  // door to plaza (south center) — widen the gap so the player can't get stuck
  set(obs,w,5,7,63); set(obs,w,6,7,63); set(ground,w,5,7,T.door);
  // objects
  const objects = [
    obj('door_plaza','door',5,7,{target:'plaza', spawn:'door_house'}),
    obj('bed','interact',1,5,{clue:'none'}),
    obj('calendar','clue',2,0,{clue:'C1'}),
    obj('sleep_point','interact',6,5),
  ];
  maps.house = makeMap('house', w, h, [
    {name:'ground',type:'tilelayer',data:ground},
    {name:'obstacles',type:'tilelayer',data:obs},
    {name:'objects',type:'objectgroup',objects},
  ]);
}

// 2. Plaza (30x20) hub
{
  const w=30, h=20;
  const ground = fillGrid(w,h,T.grass);
  // cross paths
  rectFill(ground,w,h,0,9,30,2,T.path); // horizontal
  rectFill(ground,w,h,14,0,2,20,T.path); // vertical
  // flower beds
  rectFill(ground,w,h,3,3,4,3,T.flowersY);
  rectFill(ground,w,h,23,3,4,3,T.flowersP);
  // twin bird tree
  set(ground,w,15,3,T.birdTree);
  // signs/decor
  set(ground,w,2,14,T.lamp); set(ground,w,27,14,T.lamp);
  // ribbons area for festival (decor placed day6)
  const obs = fillGrid(w,h,63);
  // border trees
  for (let x=0;x<w;x++){ set(obs,w,x,0,T.tree); set(obs,w,x,1,T.tree);}
  for (let x=0;x<w;x++){ set(obs,w,x,h-1,T.tree);}
  for (let y=0;y<h;y++){ set(obs,w,0,y,T.tree); set(obs,w,29,y,T.tree);}
  // carve door gaps in the border + place a door tile on the ground at each.
  // Gaps are 3 tiles tall/wide so the player can't miss them when walking to an edge.
  // bakery: left edge, rows 7-9
  for (let y=7;y<=9;y++) set(obs,w,0,y,63); set(ground,w,0,8,T.door);
  // lake: left edge, rows 11-13
  for (let y=11;y<=13;y++) set(obs,w,0,y,63); set(ground,w,0,12,T.door);
  // fog_boundary: left edge, rows 2-4
  for (let y=2;y<=4;y++) set(obs,w,0,y,63); set(ground,w,0,3,T.door);
  // shop: right edge, rows 7-9
  for (let y=7;y<=9;y++) set(obs,w,29,y,63); set(ground,w,29,8,T.door);
  // field: right edge, rows 11-13
  for (let y=11;y<=13;y++) set(obs,w,29,y,63); set(ground,w,29,12,T.door);
  // well_area: right edge, rows 5-6
  for (let y=5;y<=6;y++) set(obs,w,29,y,63); set(ground,w,29,5,T.door);
  // clinic: top edge, cols 13-15
  for (let x=13;x<=15;x++){ set(obs,w,x,0,63); set(obs,w,x,1,63); } set(ground,w,14,0,T.door);
  // house: bottom edge, cols 13-15
  for (let x=13;x<=15;x++) set(obs,w,x,19,63); set(ground,w,14,19,T.door);
  // announcement board (collides)
  set(obs,w,7,7,T.sign); set(obs,w,8,7,T.sign);
  // fountain-ish bush
  set(obs,w,20,12,T.bush);
  // objects: doors to all scenes — placed at the border gaps so they're easy to find
  const objects = [
    obj('door_house','door',14,19,{target:'house', spawn:'door_plaza'}),
    obj('door_bakery','door',0,8,{target:'bakery', spawn:'door_plaza'}),
    obj('door_shop','door',29,8,{target:'shop', spawn:'door_plaza'}),
    obj('door_clinic','door',14,0,{target:'clinic', spawn:'door_plaza'}),
    obj('door_lake','door',0,12,{target:'lake', spawn:'door_plaza'}),
    obj('door_field','door',29,12,{target:'field', spawn:'door_plaza'}),
    obj('door_well','door',29,5,{target:'well_area', spawn:'door_plaza'}),
    obj('door_fog','door',0,3,{target:'fog_boundary', spawn:'door_plaza'}),
    // NPC spawn points (tile coords of standing position)
    obj('npc_afu','spawn',8,8),       // dog mayor at board
    obj('npc_bird','spawn',15,2),     // twin birds on tree
    // board interaction
    obj('board','interact',7,7),
    // flowerbed water points (T2)
    obj('waterflower1','interact',4,4),
    obj('waterflower2','interact',5,4),
    obj('waterflower3','interact',4,5),
    // greet points
    obj('greet_point','interact',15,10),
  ];
  maps.plaza = makeMap('plaza', w, h, [
    {name:'ground',type:'tilelayer',data:ground},
    {name:'obstacles',type:'tilelayer',data:obs},
    {name:'objects',type:'objectgroup',objects},
  ]);
}

// 3. Bakery (15x10)
{
  const w=15,h=10;
  const ground = fillGrid(w,h,T.floor);
  rectFill(ground,w,h,0,0,15,1,T.wallTop);
  const obs = fillGrid(w,h,63);
  borderFill(obs,w,h,0,0,15,10,T.wall);
  // counter
  rectFill(obs,w,h,2,5,4,1,T.counter);
  // oven
  rectFill(obs,w,h,11,2,2,2,T.oven);
  // shelves
  set(obs,w,11,6,T.shelfFull);
  // door
  set(ground,w,7,9,T.door);
  const objects = [
    obj('door_plaza','door',7,9,{target:'plaza',spawn:'door_bakery'}),
    obj('npc_yuanyuan','spawn',3,7),
    obj('recipe_paper','clue',11,5,{clue:'C2'}), // on shelf
    obj('oven','interact',11,2),
    obj('knead_point','interact',3,6), // T11 knead dough
  ];
  maps.bakery = makeMap('bakery', w, h, [
    {name:'ground',type:'tilelayer',data:ground},
    {name:'obstacles',type:'tilelayer',data:obs},
    {name:'objects',type:'objectgroup',objects},
  ]);
}

// 4. Shop (15x10)
{
  const w=15,h=10;
  const ground = fillGrid(w,h,T.floor);
  rectFill(ground,w,h,0,0,15,1,T.wallTop);
  const obs = fillGrid(w,h,63);
  borderFill(obs,w,h,0,0,15,10,T.wall);
  // shelves: same product repeated 9 squares (C5)
  for (let y=2;y<5;y++) for (let x=2;x<5;x++) set(obs,w,x,y,T.shelfFull);
  for (let y=2;y<5;y++) for (let x=10;x<13;x++) set(obs,w,x,y,T.shelfFull);
  // counter
  rectFill(obs,w,h,6,6,3,1,T.counter);
  set(ground,w,7,9,T.door);
  const objects = [
    obj('door_plaza','door',7,9,{target:'plaza',spawn:'door_shop'}),
    obj('npc_mimi','spawn',7,7),
    obj('shelf_clue','clue',2,2,{clue:'C5'}),
    obj('count_shelf','interact',2,2), // T5
  ];
  maps.shop = makeMap('shop', w, h, [
    {name:'ground',type:'tilelayer',data:ground},
    {name:'obstacles',type:'tilelayer',data:obs},
    {name:'objects',type:'objectgroup',objects},
  ]);
}

// 5. Clinic (12x8)
{
  const w=12,h=8;
  const ground = fillGrid(w,h,T.floor);
  rectFill(ground,w,h,0,0,12,1,T.wallTop);
  const obs = fillGrid(w,h,63);
  borderFill(obs,w,h,0,0,12,8,T.wall);
  // doctor table with chart (C8)
  rectFill(obs,w,h,8,4,3,1,T.table);
  set(obs,w,9,3,T.chart);
  // bed
  set(obs,w,1,4,T.bed);
  set(ground,w,5,7,T.door);
  const objects = [
    obj('door_plaza','door',5,7,{target:'plaza',spawn:'door_clinic'}),
    obj('npc_dr_fu','spawn',5,5),
    obj('chart_clue','clue',9,3,{clue:'C8'}),
  ];
  maps.clinic = makeMap('clinic', w, h, [
    {name:'ground',type:'tilelayer',data:ground},
    {name:'obstacles',type:'tilelayer',data:obs},
    {name:'objects',type:'objectgroup',objects},
  ]);
}

// 6. Lake (24x16)
{
  const w=24,h=16;
  const ground = fillGrid(w,h,T.grass);
  // lake water body
  rectFill(ground,w,h,2,2,10,8,T.water);
  // path along bottom
  rectFill(ground,w,h,0,14,24,2,T.path);
  rectFill(ground,w,h,22,0,2,16,T.path); // right exit
  // bench (grandpa)
  set(ground,w,12,10,T.bench);
  // picnic spot
  set(ground,w,16,11,T.picnic);
  set(ground,w,17,11,T.picnic);
  const obs = fillGrid(w,h,63);
  // trees border
  for (let x=0;x<w;x++){ set(obs,w,x,0,T.tree);}
  for (let x=0;x<w;x++){ set(obs,w,x,h-1,T.tree);}
  for (let y=0;y<h;y++){ set(obs,w,0,y,T.tree);}
  const objects = [
    obj('door_plaza','door',23,8,{target:'plaza',spawn:'door_lake'}),
    obj('npc_grandpa','spawn',13,10),
    obj('picnic_point','interact',16,11), // T7
    obj('lake_reflection','interact',7,7), // reflection show
  ];
  maps.lake = makeMap('lake', w, h, [
    {name:'ground',type:'tilelayer',data:ground},
    {name:'obstacles',type:'tilelayer',data:obs},
    {name:'objects',type:'objectgroup',objects},
  ]);
}

// 7. Flower field (20x14)
{
  const w=20,h=14;
  const ground = fillGrid(w,h,T.grass);
  // berry bushes (T6)
  set(ground,w,4,4,T.berry); set(ground,w,8,3,T.berry); set(ground,w,12,5,T.berry);
  set(ground,w,6,8,T.berry); set(ground,w,14,9,T.berry);
  // flowers
  for (let i=0;i<w;i+=2) set(ground,w,i,11,T.flowersY);
  rectFill(ground,w,h,0,0,2,14,T.path);
  rectFill(ground,w,h,18,0,2,14,T.path);
  const obs = fillGrid(w,h,63);
  for (let x=0;x<w;x++){ set(obs,w,x,0,T.tree); set(obs,w,x,h-1,T.tree);}
  for (let y=0;y<h;y++){ set(obs,w,0,y,T.tree); set(obs,w,w-1,y,T.tree);}
  // remove border where exits are
  set(obs,w,0,7,63); set(obs,w,1,7,63);
  set(obs,w,18,7,63); set(obs,w,19,7,63);
  const objects = [
    obj('door_plaza','door',0,7,{target:'plaza',spawn:'door_field'}),
    obj('berry1','interact',4,4),
    obj('berry2','interact',8,3),
    obj('berry3','interact',12,5),
    obj('berry4','interact',6,8),
    obj('berry5','interact',14,9),
    obj('water_point','interact',2,11), // T2 watering here
  ];
  maps.field = makeMap('field', w, h, [
    {name:'ground',type:'tilelayer',data:ground},
    {name:'obstacles',type:'tilelayer',data:obs},
    {name:'objects',type:'objectgroup',objects},
  ]);
}

// 8. Well area (14x10)
{
  const w=14,h=10;
  const ground = fillGrid(w,h,T.grass);
  rectFill(ground,w,h,0,8,14,2,T.path);
  // well center
  set(ground,w,6,4,T.well);
  set(ground,w,7,4,T.well);
  const obs = fillGrid(w,h,63);
  for (let x=0;x<w;x++){ set(obs,w,x,0,T.tree);}
  for (let x=0;x<w;x++){ set(obs,w,x,h-1,T.tree);}
  for (let y=0;y<h;y++){ set(obs,w,0,y,T.tree); set(obs,w,w-1,y,T.tree);}
  set(obs,w,0,8,63);
  const objects = [
    obj('door_plaza','door',0,8,{target:'plaza',spawn:'door_well'}),
    obj('well','interact',6,4,{clue:'C7'}),
  ];
  maps.well_area = makeMap('well_area', w, h, [
    {name:'ground',type:'tilelayer',data:ground},
    {name:'obstacles',type:'tilelayer',data:obs},
    {name:'objects',type:'objectgroup',objects},
  ]);
}

// 9. Fog boundary (20x8)
{
  const w=20,h=8;
  const ground = fillGrid(w,h,T.grass);
  // fog wall
  rectFill(ground,w,h,10,0,4,8,T.fogThick);
  rectFill(ground,w,h,14,0,2,8,T.fog);
  rectFill(ground,w,h,0,7,20,1,T.path);
  const obs = fillGrid(w,h,63);
  for (let x=0;x<w;x++){ set(obs,w,x,0,T.tree);}
  for (let y=0;y<h;y++){ set(obs,w,0,y,T.tree); set(obs,w,w-1,y,T.tree);}
  set(obs,w,0,7,63);
  const objects = [
    obj('door_plaza','door',0,6,{target:'plaza',spawn:'door_fog'}),
    obj('fog_wall','interact',12,4,{clue:'fog'}),
    obj('fog_exit','interact',13,4,{clue:'E3_exit'}),
  ];
  maps.fog_boundary = makeMap('fog_boundary', w, h, [
    {name:'ground',type:'tilelayer',data:ground},
    {name:'obstacles',type:'tilelayer',data:obs},
    {name:'objects',type:'objectgroup',objects},
  ]);
}

// 10. White layer (corridor 20x6 + ward 10x8) combined as one map 20x14
{
  const w=20,h=14;
  const ground = fillGrid(w,h,T.whiteFloor);
  // corridor top, ward bottom
  rectFill(ground,w,h,0,0,20,6,T.whiteFloor);
  rectFill(ground,w,h,5,7,10,7,T.whiteFloor);
  rectFill(ground,w,h,0,7,5,7,T.whiteWall);
  rectFill(ground,w,h,15,7,5,7,T.whiteWall);
  const obs = fillGrid(w,h,63);
  // corridor walls
  for (let x=0;x<w;x++){ set(obs,w,x,0,T.whiteWall); set(obs,w,x,5,T.whiteWall);}
  // ward walls
  borderFill(obs,w,h,5,7,10,7,T.whiteWall);
  set(obs,w,9,13,63); // door gap
  // bed + monitor in ward
  set(obs,w,6,8,T.whiteBed);
  set(obs,w,13,8,T.whiteMonitor);
  set(obs,w,9,10,T.whiteTable);
  const objects = [
    obj('exit','door',9,12,{target:'ending'}),
    obj('patient_chart','interact',9,10,{clue:'E3_chart'}),
  ];
  maps.white_layer = makeMap('white_layer', w, h, [
    {name:'ground',type:'tilelayer',data:ground},
    {name:'obstacles',type:'tilelayer',data:obs},
    {name:'objects',type:'objectgroup',objects},
  ]);
}

for (const [name, map] of Object.entries(maps)) {
  fs.writeFileSync(path.join(outDir, name + '.json'), JSON.stringify(map, null, 0));
  console.log('Wrote ' + name + '.json');
}
console.log('Done: ' + Object.keys(maps).length + ' maps');
