#target illustrator

(function () {
    if (app.name !== "Adobe Illustrator") {
        alert("This script must be run from Adobe Illustrator.");
        return;
    }

    if (app.documents.length === 0) {
        alert("Open a document first.");
        return;
    }

    var doc = app.activeDocument;

    var DEFAULT_STROKE_WEIGHT = 0.5;
    var BRAND_BASELINE = 12;
    var BRAND_GUTTER = 12;
    var BRAND_MIN_MARGIN = 12;

    var GRID_MODES = [
        {
            name: "Standard — 12 column",
            key: "standard12",
            cols: 12,
            desc: "General screen, print, and layout use. Subdivisions: 6, 4, 3, 2."
        },
        {
            name: "Extended — 24 column",
            key: "extended24",
            cols: 24,
            desc: "For dense layouts, half-column offsets, and more complex compositions."
        }
    ];

    var COLOR_OPTIONS = [
        { name: "Cyan", rgb: [0, 255, 255] },
        { name: "Magenta", rgb: [255, 0, 255] },
        { name: "Yellow", rgb: [255, 255, 0] },
        { name: "Red", rgb: [255, 0, 0] },
        { name: "Green", rgb: [0, 200, 0] },
        { name: "Blue", rgb: [0, 120, 255] },
        { name: "Orange", rgb: [255, 140, 0] },
        { name: "Purple", rgb: [140, 0, 255] },
        { name: "Gray", rgb: [140, 140, 140] },
        { name: "Black", rgb: [0, 0, 0] }
    ];

    function pad(num, width) {
        var s = String(num);
        while (s.length < width) s = "0" + s;
        return s;
    }

    function fmtNum(n) {

        var s = String(Math.round(n * 1000) / 1000);
        return s.replace(/\.0+$/, "").replace(/(\.\d*[1-9])0+$/, "$1");
    }

    function makeRGB(rgbArr) {
        var c = new RGBColor();
        c.red = rgbArr[0];
        c.green = rgbArr[1];
        c.blue = rgbArr[2];
        return c;
    }

    function getColorByName(name) {
        for (var i = 0; i < COLOR_OPTIONS.length; i++) {
            if (COLOR_OPTIONS[i].name === name) {
                return makeRGB(COLOR_OPTIONS[i].rgb);
            }
        }
        return makeRGB([0, 255, 255]);
    }

    function getOrCreateLayer(layerName) {
        for (var i = 0; i < doc.layers.length; i++) {
            if (doc.layers[i].name === layerName) {
                return doc.layers[i];
            }
        }
        var layer = doc.layers.add();
        layer.name = layerName;
        return layer;
    }

    function addLine(parent, x1, y1, x2, y2, strokeColor, strokeWeight) {
        
        var line = parent.pathItems.add();
        line.setEntirePath([[x1, y1], [x2, y2]]);
        line.stroked = true;
        line.filled = false;
        line.strokeColor = strokeColor;
        line.strokeWidth = strokeWeight;
        return line;
    }

    function getArtboardRect(index) {
        
        var r = doc.artboards[index].artboardRect;
        return {
            left: r[0],
            top: r[1],
            right: r[2],
            bottom: r[3],
            width: r[2] - r[0],
            height: r[1] - r[3]
        };
    }

    function getTargetArtboardIndexes(scope) {
        var out = [];
        if (scope === "Active Artboard") {
            out.push(doc.artboards.getActiveArtboardIndex());
        } else {
            for (var i = 0; i < doc.artboards.length; i++) {
                out.push(i);
            }
        }
        return out;
    }

    function getLiveRect(abRect, marginValue) {
        return {
            left: abRect.left + marginValue,
            top: abRect.top - marginValue,
            right: abRect.right - marginValue,
            bottom: abRect.bottom + marginValue,
            width: (abRect.right - marginValue) - (abRect.left + marginValue),
            height: (abRect.top - marginValue) - (abRect.bottom + marginValue)
        };
    }

    function addGroup(parent, name) {
        var g = parent.groupItems.add();
        g.name = name;
        return g;
    }

    function drawMargins(group, liveRect, color, strokeWeight) {
        
        addLine(group, liveRect.left, liveRect.top, liveRect.right, liveRect.top, color, strokeWeight);
        addLine(group, liveRect.right, liveRect.top, liveRect.right, liveRect.bottom, color, strokeWeight);
        addLine(group, liveRect.right, liveRect.bottom, liveRect.left, liveRect.bottom, color, strokeWeight);
        addLine(group, liveRect.left, liveRect.bottom, liveRect.left, liveRect.top, color, strokeWeight);
    }

    function drawColumns(group, liveRect, colCount, gutter, color, strokeWeight) {
        if (colCount < 1) return;

        var totalGutter = gutter * (colCount - 1);
        var usable = liveRect.width - totalGutter;
        if (usable <= 0) return;

        var colWidth = usable / colCount;
        var x = liveRect.left;

        
        addLine(group, liveRect.left, liveRect.top, liveRect.left, liveRect.bottom, color, strokeWeight);

        for (var i = 0; i < colCount; i++) {
            var colLeft = x;
            var colRight = x + colWidth;

            addLine(group, colLeft, liveRect.top, colLeft, liveRect.bottom, color, strokeWeight);
            addLine(group, colRight, liveRect.top, colRight, liveRect.bottom, color, strokeWeight);

            if (i < colCount - 1) {
                var gutterLeft = colRight;
                var gutterRight = colRight + gutter;
                addLine(group, gutterLeft, liveRect.top, gutterLeft, liveRect.bottom, color, strokeWeight);
                addLine(group, gutterRight, liveRect.top, gutterRight, liveRect.bottom, color, strokeWeight);
            }

            x = colRight + gutter;
        }

        addLine(group, liveRect.right, liveRect.top, liveRect.right, liveRect.bottom, color, strokeWeight);
    }

    function buildBaselineYPositions(liveRect, baselineStep) {
        
        var ys = [];
        var y = liveRect.bottom;
        while (y <= liveRect.top + 0.0001) {
            ys.push(y);
            y += baselineStep;
        }
        return ys;
    }

    function drawBaselines(group, liveRect, baselineStep, color, strokeWeight) {
        if (baselineStep <= 0) return [];
        var ys = buildBaselineYPositions(liveRect, baselineStep);
        for (var i = 0; i < ys.length; i++) {
            addLine(group, liveRect.left, ys[i], liveRect.right, ys[i], color, strokeWeight);
        }
        return ys;
    }

    function buildArtboardGroupName(abIndex) {
        return "AB" + pad(abIndex + 1, 2) + "_grid";
    }

    function buildMarginGroupName(marginValue, marginMultiple) {
        return "margins__bu" + BRAND_BASELINE + "_x" + fmtNum(marginMultiple) + "_val" + fmtNum(marginValue);
    }

    function buildColumnGroupName(mode) {
        if (mode.key === "standard12") {
            return "columns__standard12_g" + fmtNum(BRAND_GUTTER) + "_subs6-4-3-2";
        }
        return "columns__extended24_g" + fmtNum(BRAND_GUTTER) + "_maps12x2";
    }

    function buildBaselineGroupName() {
        return "baseline__b" + fmtNum(BRAND_BASELINE);
    }

    function validatePositiveNumber(v, label) {
        if (isNaN(v) || v <= 0) {
            throw new Error(label + " must be greater than 0.");
        }
    }

    function validatePositiveInteger(v, label) {
        if (isNaN(v) || v < 1 || Math.floor(v) !== v) {
            throw new Error(label + " must be an integer greater than or equal to 1.");
        }
    }

    // ---------- UI ----------
    var win = new Window("dialog", "VIVA PHX 2026 Grid Generator");
    win.orientation = "column";
    win.alignChildren = ["fill", "top"];
    win.spacing = 10;
    win.margins = 16;

    var infoPanel = win.add("panel", undefined, "Rules");
    infoPanel.orientation = "column";
    infoPanel.alignChildren = ["left", "top"];
    infoPanel.margins = 12;
    infoPanel.spacing = 4;
    infoPanel.add("statictext", undefined, "Baseline: 12pt / 12px");
    infoPanel.add("statictext", undefined, "Gutter: 12pt / 12px");
    infoPanel.add("statictext", undefined, "Margin: minimum 12pt, scaled only in multiples of 12");
    infoPanel.add("statictext", undefined, "Modes: Standard 12-column or Extended 24-column");

    var modePanel = win.add("panel", undefined, "Grid Mode");
    modePanel.orientation = "column";
    modePanel.alignChildren = ["fill", "top"];
    modePanel.margins = 12;
    modePanel.spacing = 8;

    var modeRow = modePanel.add("group");
    modeRow.orientation = "row";
    modeRow.alignChildren = ["left", "center"];
    modeRow.add("statictext", undefined, "Mode:");

    var modeNames = [];
    for (var gm = 0; gm < GRID_MODES.length; gm++) modeNames.push(GRID_MODES[gm].name);
    var modeDropdown = modeRow.add("dropdownlist", undefined, modeNames);
    modeDropdown.minimumSize.width = 280;
    modeDropdown.selection = 0;

    var modeDesc = modePanel.add("statictext", undefined, GRID_MODES[0].desc, { multiline: true });

    var targetPanel = win.add("panel", undefined, "Target");
    targetPanel.orientation = "row";
    targetPanel.alignChildren = ["left", "center"];
    targetPanel.margins = 12;
    targetPanel.add("statictext", undefined, "Apply to:");
    var scopeDropdown = targetPanel.add("dropdownlist", undefined, ["Active Artboard", "All Artboards"]);
    scopeDropdown.selection = 0;

    var marginPanel = win.add("panel", undefined, "Margins");
    marginPanel.orientation = "row";
    marginPanel.alignChildren = ["left", "center"];
    marginPanel.margins = 12;
    marginPanel.spacing = 8;
    marginPanel.add("statictext", undefined, "Margin multiple:");
    var marginMultipleInput = marginPanel.add("edittext", undefined, "4");
    marginMultipleInput.characters = 6;
    marginPanel.add("statictext", undefined, "× 12pt");

    var stylePanel = win.add("panel", undefined, "Style");
    stylePanel.orientation = "row";
    stylePanel.alignChildren = ["left", "center"];
    stylePanel.margins = 12;

    stylePanel.add("statictext", undefined, "Line color:");
    var colorNames = [];
    for (var c = 0; c < COLOR_OPTIONS.length; c++) colorNames.push(COLOR_OPTIONS[c].name);
    var colorDropdown = stylePanel.add("dropdownlist", undefined, colorNames);
    colorDropdown.selection = 0;

    stylePanel.add("statictext", undefined, "Stroke (pt):");
    var strokeInput = stylePanel.add("edittext", undefined, String(DEFAULT_STROKE_WEIGHT));
    strokeInput.characters = 6;

    var buttons = win.add("group");
    buttons.orientation = "row";
    buttons.alignment = "right";
    buttons.add("button", undefined, "Cancel", { name: "cancel" });
    buttons.add("button", undefined, "Build", { name: "ok" });

    modeDropdown.onChange = function () {
        var idx = modeDropdown.selection ? modeDropdown.selection.index : 0;
        modeDesc.text = GRID_MODES[idx].desc;
    };

    if (win.show() !== 1) return;

    try {
        var mode = GRID_MODES[modeDropdown.selection.index];
        var scope = scopeDropdown.selection.text;
        var colorName = colorDropdown.selection.text;
        var strokeWeight = parseFloat(strokeInput.text);
        var marginMultiple = parseInt(marginMultipleInput.text, 10);
        var marginValue = BRAND_MIN_MARGIN * marginMultiple;

        validatePositiveNumber(strokeWeight, "Stroke weight");
        validatePositiveInteger(marginMultiple, "Margin multiple");

        var guidesLayer = getOrCreateLayer("guides");
        var strokeColor = getColorByName(colorName);
        var targets = getTargetArtboardIndexes(scope);

        for (var i = 0; i < targets.length; i++) {
            var abIndex = targets[i];
            var abRect = getArtboardRect(abIndex);
            var liveRect = getLiveRect(abRect, marginValue);

            if (liveRect.width <= 0 || liveRect.height <= 0) {
                throw new Error("Margins consume the full artboard on AB" + pad(abIndex + 1, 2) + ".");
            }

            var totalGutter = BRAND_GUTTER * (mode.cols - 1);
            var usable = liveRect.width - totalGutter;
            if (usable <= 0) {
                throw new Error(
                    "The selected mode does not fit on AB" +
                    pad(abIndex + 1, 2) +
                    " with margin " + fmtNum(marginValue) + "pt."
                );
            }

            var abGroup = addGroup(guidesLayer, buildArtboardGroupName(abIndex));
            var gridGroup = addGroup(abGroup, "grid");
            var marginsGroup = addGroup(gridGroup, buildMarginGroupName(marginValue, marginMultiple));
            var columnsGroup = addGroup(gridGroup, buildColumnGroupName(mode));
            var baselineGroup = addGroup(gridGroup, buildBaselineGroupName());

            drawMargins(marginsGroup, liveRect, strokeColor, strokeWeight);
            drawColumns(columnsGroup, liveRect, mode.cols, BRAND_GUTTER, strokeColor, strokeWeight);
            drawBaselines(baselineGroup, liveRect, BRAND_BASELINE, strokeColor, strokeWeight);
        }

        alert("Brand grid generation complete.");
    } catch (err) {
        alert("Error: " + err.message);
    }
})();
