// ES3-only host implementation. Arguments are supplied as JSON literals by Node.
function runIllustrator(operation, args) {
    var previousInteraction = app.userInteractionLevel;
    try {
        app.userInteractionLevel = UserInteractionLevel.DONTDISPLAYALERTS;
        return dispatchIllustrator(operation, args);
    } finally {
        app.userInteractionLevel = previousInteraction;
    }
}
function dispatchIllustrator(operation, args) {
    function documentInfo(d) {
        return {
            name: d.name,
            width: d.width,
            height: d.height,
            saved: d.saved,
            artboardCount: d.artboards.length,
            layerCount: d.layers.length,
            itemCount: d.pageItems.length
        };
    }
    function itemInfo(item) {
        // Flush text layout before querying bounds in the same script as a text edit.
        if (item.typename === "TextFrame") app.redraw();
        var result = {
            uuid: item.uuid,
            name: item.name,
            type: item.typename,
            opacity: item.opacity,
            bounds: Array.prototype.slice.call(item.geometricBounds)
        };
        result.hidden = item.hidden;
        result.locked = item.locked;
        if (item.typename === "TextFrame") {
            result.text = item.contents;
            result.size = item.textRange.characterAttributes.size;
            result.font = item.textRange.characterAttributes.textFont.name;
        }
        return result;
    }
    function rgb(values) {
        var c = new RGBColor();
        c.red = values[0];
        c.green = values[1];
        c.blue = values[2];
        return c;
    }
    function unique(collection, value) {
        var found = null;
        for (var i = 0; i < collection.length; i++)
            if (collection[i].name === value) {
                if (found) throw new Error("AMBIGUOUS_TARGET: " + value);
                found = collection[i];
            }
        if (!found) throw new Error("TARGET_NOT_FOUND: " + value);
        return found;
    }
    function requireType(item, type) {
        if (item.typename !== type)
            throw new Error("Expected " + type + ", received " + item.typename);
    }
    function paged(collection, summarize) {
        var rows = [];
        for (var i = args.offset; i < collection.length && i < args.offset + args.limit; i++)
            rows.push(summarize(collection[i], i));
        return {
            total: collection.length,
            offset: args.offset,
            items: rows,
            hasMore: collection.length > args.offset + args.limit
        };
    }
    var d, item, layer, i, result;
    if (operation === "font.list") {
        var fonts = [];
        for (i = 0; i < app.textFonts.length; i++) {
            var font = app.textFonts[i];
            if ((font.name + " " + font.family).toLowerCase().indexOf(args.search.toLowerCase()) >= 0)
                fonts.push({ name: font.name, family: font.family, style: font.style });
        }
        return paged(fonts, function (f) { return f; });
    }
    if (operation === "document.list") {
        result = [];
        for (i = 0; i < app.documents.length; i++) result.push(documentInfo(app.documents[i]));
        return { documents: result };
    }
    if (operation === "document.create") {
        d = app.documents.add(DocumentColorSpace.RGB, args.width, args.height);
        return documentInfo(d); // Illustrator assigns the unsaved document name; requested name is only a label until saveAs.
    }
    if (operation === "document.open") {
        var input = new File(args.path);
        if (!input.exists) throw new Error("INPUT_NOT_FOUND");
        return documentInfo(app.open(input));
    }
    d = unique(app.documents, args.document);
    if (operation === "document.info") return documentInfo(d);
    if (
        operation === "document.save_as" ||
        operation === "document.export_svg" ||
        operation === "document.export_png" || operation === "document.preview"
    ) {
        var output = new File(args.path);
        if (output.exists) throw new Error("OUTPUT_EXISTS");
        if (operation === "document.save_as") d.saveAs(output, new IllustratorSaveOptions());
        if (operation === "document.export_svg")
            d.exportFile(output, ExportType.SVG, new ExportOptionsSVG());
        if (operation === "document.export_png" || operation === "document.preview") {
            var options = new ExportOptionsPNG24();
            options.artBoardClipping = true;
            options.transparency = true;
            var scale = args.scale;
            if (operation === "document.preview") {
                var bounds = d.artboards[d.artboards.getActiveArtboardIndex()].artboardRect;
                scale = 100 * args.maxSize / Math.max(bounds[2] - bounds[0], bounds[1] - bounds[3]);
            }
            options.horizontalScale = scale;
            options.verticalScale = scale;
            d.exportFile(output, ExportType.PNG24, options);
        }
        if (!output.exists)
            throw new Error(
                "OUTPUT_NOT_FOUND: inspect the export directory; Illustrator may have altered the output name"
            );
        return { path: output.fsName, document: documentInfo(d) };
    }
    if (operation === "artboard.list") {
        result = [];
        for (i = 0; i < d.artboards.length; i++)
            result.push({
                index: i,
                name: d.artboards[i].name,
                bounds: Array.prototype.slice.call(d.artboards[i].artboardRect)
            });
        return { activeIndex: d.artboards.getActiveArtboardIndex(), artboards: result };
    }
    if (operation === "artboard.add") {
        item = d.artboards.add(args.bounds);
        item.name = args.name;
        return { name: item.name };
    }
    if (operation === "artboard.rename" || operation === "artboard.activate") {
        if (args.index >= d.artboards.length) throw new Error("ARTBOARD_NOT_FOUND");
        if (operation === "artboard.rename") d.artboards[args.index].name = args.name;
        else d.artboards.setActiveArtboardIndex(args.index);
        return { index: args.index, name: d.artboards[args.index].name };
    }
    if (operation === "layer.list")
        return paged(d.layers, function (l, index) {
            return { index: index, name: l.name, visible: l.visible, locked: l.locked };
        });
    if (operation === "layer.create") {
        layer = d.layers.add();
        layer.name = args.name;
        return { name: layer.name };
    }
    if (
        operation === "layer.rename" ||
        operation === "layer.visibility" ||
        operation === "layer.lock"
    ) {
        layer = unique(d.layers, args.layer);
        if (operation === "layer.rename") layer.name = args.name;
        if (operation === "layer.visibility") layer.visible = args.visible;
        if (operation === "layer.lock") layer.locked = args.locked;
        return { name: layer.name, visible: layer.visible, locked: layer.locked };
    }
    if (operation === "item.list") return paged(d.pageItems, itemInfo);
    if (operation === "shape.rectangle" || operation === "shape.ellipse") {
        item =
            operation === "shape.rectangle"
                ? d.pathItems.rectangle(args.top, args.left, args.width, args.height)
                : d.pathItems.ellipse(args.top, args.left, args.width, args.height);
        item.name = args.name;
        item.filled = true;
        item.fillColor = rgb(args.fill);
        item.stroked = false;
        return itemInfo(item);
    }
    if (operation === "path.create" || operation === "path.bezier") {
        item = d.pathItems.add();
        if (operation === "path.create") item.setEntirePath(args.points);
        else {
            for (i = 0; i < args.points.length; i++) {
                var p = item.pathPoints.add(), value = args.points[i];
                p.anchor = value.anchor;
                p.leftDirection = value.left;
                p.rightDirection = value.right;
                p.pointType = value.smooth ? PointType.SMOOTH : PointType.CORNER;
            }
        }
        item.closed = args.closed;
        item.name = args.name;
        item.filled = false;
        item.stroked = true;
        item.strokeColor = rgb(args.stroke);
        item.strokeWidth = args.strokeWidth;
        return itemInfo(item);
    }
    if (operation === "text.create") {
        item = d.textFrames.add();
        item.name = args.name;
        item.contents = args.text;
        item.position = args.position;
        item.textRange.characterAttributes.size = args.size;
        item.textRange.characterAttributes.fillColor = rgb(args.fill);
        return itemInfo(item);
    }
    if (operation === "group.create") {
        var selected = [],
            seen = {};
        for (i = 0; i < args.uuids.length; i++) {
            if (seen["id:" + args.uuids[i]]) throw new Error("Duplicate item UUID");
            seen["id:" + args.uuids[i]] = true;
            selected.push(d.getPageItemFromUuid(args.uuids[i]));
        }
        // Reject parent/child pairs before changing any hierarchy.
        for (i = 0; i < selected.length; i++) {
            var parent = selected[i].parent;
            while (parent && parent.typename !== "Document") {
                for (var j = 0; j < selected.length; j++)
                    if (parent === selected[j])
                        throw new Error("Cannot group an ancestor together with its child");
                parent = parent.parent;
            }
        }
        var group = d.groupItems.add();
        group.name = args.name;
        for (i = 0; i < selected.length; i++) selected[i].move(group, ElementPlacement.PLACEATEND);
        return itemInfo(group);
    }
    item = d.getPageItemFromUuid(args.uuid);
    if (!item) throw new Error("ITEM_NOT_FOUND");
    switch (operation) {
        case "item.info":
            result = itemInfo(item);
            if (item.typename === "PathItem") {
                result.closed = item.closed;
                result.filled = item.filled;
                result.stroked = item.stroked;
                result.strokeWidth = item.strokeWidth;
                result.fillColor = String(item.fillColor.typename);
                result.points = [];
                result.pointCount = item.pathPoints.length;
                for (i = 0; i < item.pathPoints.length && i < 1000; i++) {
                    var point = item.pathPoints[i];
                    result.points.push({ anchor: Array.prototype.slice.call(point.anchor), left: Array.prototype.slice.call(point.leftDirection), right: Array.prototype.slice.call(point.rightDirection) });
                }
            }
            if (item.typename === "GroupItem") result.childCount = item.pageItems.length;
            return result;
        case "text.style":
            requireType(item, "TextFrame");
            var attrs = item.textRange.characterAttributes;
            var selectedFont = args.font ? app.textFonts.getByName(args.font) : null;
            if (selectedFont) attrs.textFont = selectedFont;
            if (args.size !== undefined) attrs.size = args.size;
            if (args.fill !== undefined) attrs.fillColor = rgb(args.fill);
            if (args.tracking !== undefined) attrs.tracking = args.tracking;
            if (args.leading !== undefined) { attrs.autoLeading = false; attrs.leading = args.leading; }
            if (args.justification !== undefined) {
                var align = { left: Justification.LEFT, center: Justification.CENTER, right: Justification.RIGHT };
                item.textRange.paragraphAttributes.justification = align[args.justification];
            }
            break;
        case "path.point_set":
            requireType(item, "PathItem");
            if (args.index >= item.pathPoints.length) throw new Error("POINT_NOT_FOUND");
            var edited = item.pathPoints[args.index];
            edited.anchor = args.anchor;
            edited.leftDirection = args.left;
            edited.rightDirection = args.right;
            edited.pointType = args.smooth ? PointType.SMOOTH : PointType.CORNER;
            break;
        case "item.position": item.position = args.position; break;
        case "item.visibility": item.hidden = !args.visible; break;
        case "item.lock": item.locked = args.locked; break;
        case "item.order":
            var orders = { front: ZOrderMethod.BRINGTOFRONT, back: ZOrderMethod.SENDTOBACK, forward: ZOrderMethod.BRINGFORWARD, backward: ZOrderMethod.SENDBACKWARD };
            item.zOrder(orders[args.order]);
            break;
        case "item.move_to_layer":
            item.move(unique(d.layers, args.layer), ElementPlacement.PLACEATEND);
            break;
        case "group.ungroup":
            requireType(item, "GroupItem");
            var children = [];
            while (item.pageItems.length) {
                var child = item.pageItems[0];
                child.move(item, ElementPlacement.PLACEBEFORE);
                children.push(itemInfo(child));
            }
            item.remove();
            return { items: children };
        case "text.set":
            requireType(item, "TextFrame");
            item.contents = args.text;
            break;
        case "text.size":
            requireType(item, "TextFrame");
            item.textRange.characterAttributes.size = args.size;
            break;
        case "item.rename":
            item.name = args.name;
            break;
        case "item.translate":
            item.translate(args.x, args.y);
            break;
        case "item.rotate":
            item.rotate(args.angle);
            break;
        case "item.scale":
            item.resize(args.width, args.height);
            break;
        case "item.opacity":
            item.opacity = args.opacity;
            break;
        case "item.duplicate":
            item = item.duplicate();
            break;
        case "item.delete":
            item.remove();
            return { deletedUuid: args.uuid };
        case "path.fill":
            requireType(item, "PathItem");
            item.filled = true;
            item.fillColor = rgb(args.color);
            break;
        case "path.stroke":
            requireType(item, "PathItem");
            item.stroked = true;
            item.strokeColor = rgb(args.color);
            item.strokeWidth = args.width;
            break;
        default:
            throw new Error("Unsupported Illustrator operation: " + operation);
    }
    return itemInfo(item);
}
