#target photoshop

/**
 * assemble-map-tiles.jsx
 *
 * Assembles map tiles downloaded by the Map Download Tool into a single
 * Photoshop document. Tile filenames must follow the pattern: z_x_y.png
 *
 * Usage: File > Scripts > Browse... and select this file.
 *        For large tile sets (100+ tiles), assembly may take several minutes.
 */

(function () {

    var TILE_SIZE = 256;

    // ── 1. Select folder ──────────────────────────────────────────────────────
    var folder = Folder.selectDialog(
        "Select the folder containing your map tile files (z_x_y.png)"
    );
    if (!folder) { return; } // user cancelled

    // ── 2. Collect and parse tile files ──────────────────────────────────────
    var allFiles = folder.getFiles("*.png");
    if (!allFiles || allFiles.length === 0) {
        alert("No PNG files found in:\n" + folder.fsName);
        return;
    }

    var tiles = [];
    var re = /^(\d+)_(\d+)_(\d+)\.png$/i;

    for (var i = 0; i < allFiles.length; i++) {
        if (!(allFiles[i] instanceof File)) { continue; }
        var m = allFiles[i].name.match(re);
        if (!m) { continue; }
        tiles.push({
            file: allFiles[i],
            z:    parseInt(m[1], 10),
            x:    parseInt(m[2], 10),
            y:    parseInt(m[3], 10)
        });
    }

    if (tiles.length === 0) {
        alert("No files matching the pattern z_x_y.png were found in:\n" + folder.fsName);
        return;
    }

    // ── 3. Compute grid bounds ────────────────────────────────────────────────
    var xMin = tiles[0].x, xMax = tiles[0].x;
    var yMin = tiles[0].y, yMax = tiles[0].y;

    for (var i = 1; i < tiles.length; i++) {
        if (tiles[i].x < xMin) { xMin = tiles[i].x; }
        if (tiles[i].x > xMax) { xMax = tiles[i].x; }
        if (tiles[i].y < yMin) { yMin = tiles[i].y; }
        if (tiles[i].y > yMax) { yMax = tiles[i].y; }
    }

    var cols      = xMax - xMin + 1;
    var rows      = yMax - yMin + 1;
    var docWidth  = cols * TILE_SIZE;
    var docHeight = rows * TILE_SIZE;

    // ── 4. Create destination document ───────────────────────────────────────
    var savedUnits = app.preferences.rulerUnits;
    app.preferences.rulerUnits = Units.PIXELS;

    var mainDoc = app.documents.add(
        docWidth,
        docHeight,
        72,
        "Assembled Map Tiles",
        NewDocumentMode.RGB,
        DocumentFill.WHITE
    );

    // ── 5. Place each tile as its own layer ───────────────────────────────────
    for (var i = 0; i < tiles.length; i++) {
        var t     = tiles[i];
        var destX = (t.x - xMin) * TILE_SIZE;
        var destY = (t.y - yMin) * TILE_SIZE;

        // Open tile, copy all pixels, close
        var tileDoc = app.open(t.file);
        app.activeDocument = tileDoc;
        tileDoc.selection.selectAll();
        tileDoc.selection.copy();
        tileDoc.close(SaveOptions.DONOTSAVECHANGES);

        // Paste into mainDoc as a new layer (centered by Photoshop)
        app.activeDocument = mainDoc;
        mainDoc.selection.deselect();
        mainDoc.paste();
        var newLayer = mainDoc.activeLayer;
        newLayer.name = t.z + "_" + t.x + "_" + t.y;

        // Translate from where Photoshop centered it to the correct grid position
        var b    = newLayer.bounds;
        var offX = destX - parseFloat(b[0]);
        var offY = destY - parseFloat(b[1]);
        newLayer.translate(offX, offY);
    }

    // ── 6. Restore ruler units ────────────────────────────────────────────────
    app.preferences.rulerUnits = savedUnits;

    // ── 7. Done ───────────────────────────────────────────────────────────────
    alert(
        "Assembly complete!\n\n" +
        tiles.length + (tiles.length === 1 ? " tile" : " tiles") + " placed\n" +
        cols + " \xd7 " + rows + " grid  (" + docWidth + " \xd7 " + docHeight + " px)"
    );

}());
