(function (me) {

    //==================================================
    // 1) applyKeyframes: Undoは呼び出し側に任せる
    //==================================================
    function applyKeyframes(layer, propertyName, keyframes) {
        var prop = layer.property(propertyName);
        if (!prop) {
            alert("Layer \"" + layer.name + "\" does not have \"" + propertyName + "\" property.");
            return;
        }

        var comp = layer.containingComp;
        var frameDuration = 1 / comp.frameRate;
        var currentTime = comp.time;

        // ここでは UndoGroup は行わず呼び出し側でまとめる。
        for (var i = 0; i < keyframes.length; i++) {
            var keyTime = currentTime + (keyframes[i].frame * frameDuration);
            prop.setValueAtTime(keyTime, keyframes[i].value);
        }
    }

    //==================================================
    // 2) applyRotationKeyframesFromSelection - 複数レイヤー + 一括Undo
    //==================================================
    function applyRotationKeyframesFromSelection() {
        var comp = app.project.activeItem;
        if (!(comp && comp instanceof CompItem)) {
            alert("Please open a composition.");
            return;
        }
        var layers = comp.selectedLayers;
        if (layers.length === 0) {
            alert("Please select at least one layer.");
            return;
        }

        app.beginUndoGroup("Rotation Shake (multi-layer)");

        for (var i = 0; i < layers.length; i++) {
            var layer = layers[i];
            var prop = layer.property("Rotation");
            if (!prop) {
                alert("Layer \"" + layer.name + "\" does not have a Rotation property.");
                continue;
            }

            var currentRotation = prop.value;
            var keyframes = [
                { frame: 0,  value: currentRotation },
                { frame: 5,  value: currentRotation + 25 },
                { frame: 10, value: currentRotation - 25 },
                { frame: 15, value: currentRotation + 25 },
                { frame: 20, value: currentRotation - 25 },
                { frame: 25, value: currentRotation + 25 },
                { frame: 30, value: currentRotation - 25 },
                { frame: 35, value: currentRotation }
            ];

            applyKeyframes(layer, "Rotation", keyframes);
        }

        app.endUndoGroup();
    }

    //==================================================
    // 3) applyKeyframesFromSelection - 複数レイヤー + 一括Undo (Scale, Opacityなど)
    //==================================================
    function applyKeyframesFromSelection(property, keyframes) {
        var comp = app.project.activeItem;
        if (!(comp && comp instanceof CompItem)) {
            alert("Please open a composition.");
            return;
        }
        var layers = comp.selectedLayers;
        if (layers.length === 0) {
            alert("Please select at least one layer.");
            return;
        }

        app.beginUndoGroup("Apply Keyframes (multi-layer)");

        for (var i = 0; i < layers.length; i++) {
            var layer = layers[i];
            applyKeyframes(layer, property, keyframes);
        }

        app.endUndoGroup();
    }

    //==================================================
    // 4) applyShakeKeyframesFromSelection - 複数レイヤー + 一括Undo
    //==================================================
    function applyShakeKeyframesFromSelection(isHorizontal) {
        var comp = app.project.activeItem;
        if (!(comp && comp instanceof CompItem)) {
            alert("Please open a composition.");
            return;
        }
        var layers = comp.selectedLayers;
        if (layers.length === 0) {
            alert("Please select at least one layer.");
            return;
        }

        app.beginUndoGroup("Shake (multi-layer)");

        for (var i = 0; i < layers.length; i++) {
            var layer = layers[i];
            var prop = layer.property("Position");
            if (!prop) {
                alert("Layer \"" + layer.name + "\" does not have a Position property.");
                continue;
            }

            var basePos = prop.value;
            var keyframes = [
                { frame: 0,  value: isHorizontal ? [basePos[0] + 50, basePos[1]] : [basePos[0], basePos[1] + 50] },
                { frame: 5,  value: isHorizontal ? [basePos[0] - 50, basePos[1]] : [basePos[0], basePos[1] - 50] },
                { frame: 10, value: isHorizontal ? [basePos[0] + 50, basePos[1]] : [basePos[0], basePos[1] + 50] },
                { frame: 15, value: isHorizontal ? [basePos[0] - 50, basePos[1]] : [basePos[0], basePos[1] - 50] },
                { frame: 20, value: isHorizontal ? [basePos[0] + 50, basePos[1]] : [basePos[0], basePos[1] + 50] },
                { frame: 25, value: isHorizontal ? [basePos[0] - 50, basePos[1]] : [basePos[0], basePos[1] - 50] },
                { frame: 30, value: isHorizontal ? [basePos[0] + 50, basePos[1]] : [basePos[0], basePos[1] + 50] },
                { frame: 35, value: isHorizontal ? [basePos[0] - 50, basePos[1]] : [basePos[0], basePos[1] - 50] }
            ];

            applyKeyframes(layer, "Position", keyframes);
        }

        app.endUndoGroup();
    }

    //==================================================
    // 5) Custom Keyframes: 複数レイヤー + Positionならオフセット加算
    //==================================================
    function createCustomKeyframeUI(parent) {
        var panel = parent.add("panel", undefined, "Custom Keyframes");
        panel.orientation = "column";

        var defaultSettings = {
            "Scale": {
                frames: "0,10",
                values: "0,0,  100,100"
            },
            "Opacity": {
                frames: "0,10",
                values: "0,100"
            },
            "Rotation": {
                frames: "0,10",
                values: "0,25"
            },
            "Position": {
                frames: "0,10",
                values: "0,0,  100,100"
            }
        };

        panel.add("statictext", undefined, "Property:");
        var propertyDropdown = panel.add("dropdownlist", undefined, ["Scale", "Opacity", "Rotation", "Position"]);
        propertyDropdown.selection = 0;

        panel.add("statictext", undefined, "Frames (comma separated):");
        var frameInput = panel.add("edittext", undefined, defaultSettings[propertyDropdown.selection.text].frames);
        frameInput.characters = 25;

        panel.add("statictext", undefined, "Values (comma separated):");
        var valueInput = panel.add("edittext", undefined, defaultSettings[propertyDropdown.selection.text].values);
        valueInput.characters = 30;
        valueInput.preferredSize.height = 40;

        // Property変更時にデフォルト更新
        propertyDropdown.onChange = function () {
            var pName = propertyDropdown.selection.text;
            if (defaultSettings[pName]) {
                frameInput.text = defaultSettings[pName].frames;
                valueInput.text = defaultSettings[pName].values;
            }
        };

        var applyButton = panel.add("button", undefined, "Apply Custom Keyframes");
        applyButton.onClick = function () {
            var comp = app.project.activeItem;
            if (!(comp && comp instanceof CompItem)) {
                alert("Please open a composition.");
                return;
            }
            var layers = comp.selectedLayers;
            if (layers.length === 0) {
                alert("Please select at least one layer.");
                return;
            }

            var selectedProperty = propertyDropdown.selection.text;

            // Frames
            var frameStrArray = frameInput.text.split(",");
            var frames = [];
            for (var i = 0; i < frameStrArray.length; i++) {
                var fNum = parseFloat(frameStrArray[i]);
                if (isNaN(fNum)) {
                    alert("Invalid frame value: " + frameStrArray[i]);
                    return;
                }
                frames.push(fNum);
            }

            // Values
            var valueStrArray = valueInput.text.split(",");
            for (var j = 0; j < valueStrArray.length; j++) {
                valueStrArray[j] = valueStrArray[j].replace(/^\s+|\s+$/g, "");
            }

            var is2D = (selectedProperty === "Scale" || selectedProperty === "Position");
            if (is2D) {
                if (valueStrArray.length !== frames.length * 2) {
                    alert("For 2D property, need frames.length x 2 values.\nCheck your input.");
                    return;
                }
            } else {
                if (valueStrArray.length !== frames.length) {
                    alert("For 1D property, need frames.length values.\nCheck your input.");
                    return;
                }
            }

            // ベースのキーフレーム配列 (Positionならオフセット値、他は絶対値)
            var baseKeyframes = [];
            var idx = 0;
            for (var k = 0; k < frames.length; k++) {
                if (is2D) {
                    var vx = parseFloat(valueStrArray[idx]);
                    var vy = parseFloat(valueStrArray[idx + 1]);
                    if (isNaN(vx) || isNaN(vy)) {
                        alert("Invalid numeric value in 2D property at index " + idx);
                        return;
                    }
                    baseKeyframes.push({ frame: frames[k], value: [vx, vy] });
                    idx += 2;
                } else {
                    var v1 = parseFloat(valueStrArray[idx]);
                    if (isNaN(v1)) {
                        alert("Invalid numeric value in property at index " + idx);
                        return;
                    }
                    baseKeyframes.push({ frame: frames[k], value: v1 });
                    idx += 1;
                }
            }

            app.beginUndoGroup("Custom Keyframes (multi-layer)");

            // レイヤーごとに処理
            for (var m = 0; m < layers.length; m++) {
                var layer = layers[m];
                if (selectedProperty === "Position") {
                    // Positionは現在の値にオフセット加算
                    var propPos = layer.property("Position");
                    if (!propPos) {
                        alert("Layer \"" + layer.name + "\" does not have a Position property.");
                        continue;
                    }
                    var basePos = propPos.value; // CTI時点のPosition
                    var offsetKeyframes = [];
                    for (var n = 0; n < baseKeyframes.length; n++) {
                        var offX = baseKeyframes[n].value[0];
                        var offY = baseKeyframes[n].value[1];
                        offsetKeyframes.push({
                            frame: baseKeyframes[n].frame,
                            value: [basePos[0] + offX, basePos[1] + offY]
                        });
                    }
                    applyKeyframes(layer, "Position", offsetKeyframes);

                } else {
                    // Scale / Opacity / Rotation ⇒ 従来どおり絶対値
                    applyKeyframes(layer, selectedProperty, baseKeyframes);
                }
            }

            app.endUndoGroup();
        };
    }

    //==================================================
    // 6) Repeated Keyframes: 複数レイヤー対応 + Positionならオフセット加算
    //==================================================
    function createRepeatedKeyframeUI(parent) {
        var panel = parent.add("panel", undefined, "Repeated Keyframes");
        panel.orientation = "column";

        // デフォルト設定 (Positionはオフセット想定)
        var defaultSettings = {
            "Scale": {
                frames: "0,5",
                values: "0,0, 100,100"
            },
            "Opacity": {
                frames: "0,5",
                values: "100,50"
            },
            "Rotation": {
                frames: "0,5",
                values: "0,25"
            },
            "Position": {
                frames: "0,5",
                values: "0,0, 100,0"
            }
        };

        panel.add("statictext", undefined, "Property (Scale / Opacity / Rotation / Position):");
        var propertyDropdown = panel.add("dropdownlist", undefined, ["Scale", "Opacity", "Rotation", "Position"]);
        propertyDropdown.selection = 1; // 例: Opacityを初期

        panel.add("statictext", undefined, "Frames (comma separated):");
        var frameInput = panel.add("edittext", undefined, defaultSettings[propertyDropdown.selection.text].frames);
        frameInput.characters = 25;

        panel.add("statictext", undefined,
            "Values (comma separated):\n(1D for Opacity/Rotation, 2D for Scale/Position)\n(Position is offset from current pos)");
        var valueInput = panel.add("edittext", undefined, defaultSettings[propertyDropdown.selection.text].values);
        valueInput.characters = 30;
        valueInput.preferredSize.height = 40;

        panel.add("statictext", undefined, "Repeat count:");
        var repeatInput = panel.add("edittext", undefined, "3");
        repeatInput.characters = 5;

        // Property切り替え時にデフォルト更新
        propertyDropdown.onChange = function () {
            var pName = propertyDropdown.selection.text;
            if (defaultSettings[pName]) {
                frameInput.text = defaultSettings[pName].frames;
                valueInput.text = defaultSettings[pName].values;
            }
        };

        var applyButton = panel.add("button", undefined, "Apply Repeated Keyframes");
        applyButton.onClick = function () {
            var comp = app.project.activeItem;
            if (!(comp && comp instanceof CompItem)) {
                alert("Please open a composition.");
                return;
            }
            var layers = comp.selectedLayers;
            if (layers.length === 0) {
                alert("Please select at least one layer.");
                return;
            }

            var selectedPropName = propertyDropdown.selection.text;

            // Frames
            var frameStrArray = frameInput.text.split(",");
            var frames = [];
            for (var i = 0; i < frameStrArray.length; i++) {
                var fNum = parseFloat(frameStrArray[i]);
                if (isNaN(fNum)) {
                    alert("Invalid frame value: " + frameStrArray[i]);
                    return;
                }
                frames.push(fNum);
            }

            // Values
            var valueStrArray = valueInput.text.split(",");
            for (var j = 0; j < valueStrArray.length; j++) {
                valueStrArray[j] = valueStrArray[j].replace(/^\s+|\s+$/g, "");
            }

            var is2D = (selectedPropName === "Scale" || selectedPropName === "Position");
            if (is2D) {
                if (valueStrArray.length !== frames.length * 2) {
                    alert("For 2D property, you need frames.length x 2 values.\nCheck your input.");
                    return;
                }
            } else {
                if (valueStrArray.length !== frames.length) {
                    alert("For 1D property, you need frames.length values.\nCheck your input.");
                    return;
                }
            }

            var repeatCount = parseInt(repeatInput.text, 10);
            if (isNaN(repeatCount) || repeatCount < 1) {
                alert("Invalid repeat count.");
                return;
            }

            // 1セット分のキーフレーム (Positionならオフセット値)
            var baseKeyframes = [];
            var idx = 0;
            for (var k = 0; k < frames.length; k++) {
                if (is2D) {
                    var vx = parseFloat(valueStrArray[idx]);
                    var vy = parseFloat(valueStrArray[idx + 1]);
                    if (isNaN(vx) || isNaN(vy)) {
                        alert("Invalid numeric value in 2D property at index " + idx);
                        return;
                    }
                    baseKeyframes.push({ frame: frames[k], value: [vx, vy] });
                    idx += 2;
                } else {
                    var v1 = parseFloat(valueStrArray[idx]);
                    if (isNaN(v1)) {
                        alert("Invalid numeric value in property at index " + idx);
                        return;
                    }
                    baseKeyframes.push({ frame: frames[k], value: v1 });
                    idx += 1;
                }
            }

            // 繰り返し適用
            var minFrame = frames[0];
            var maxFrame = frames[frames.length - 1];
            var chunkSpan = maxFrame - minFrame;
            var offsetSpan = chunkSpan * 2;

            // allKeyframesには「オフセット前」の相対値が入る(= frames だけずらした形)
            var allKeyframes = [];
            for (var r = 0; r < repeatCount; r++) {
                var offset = r * offsetSpan;
                for (var t = 0; t < baseKeyframes.length; t++) {
                    allKeyframes.push({
                        frame: baseKeyframes[t].frame + offset,
                        value: baseKeyframes[t].value
                    });
                }
            }

            // まとめてUndo
            app.beginUndoGroup("Repeated Keyframes (multi-layer)");

            // レイヤーごとに適用
            for (var m = 0; m < layers.length; m++) {
                var layer = layers[m];

                if (selectedPropName === "Position") {
                    // Positionは現在のposに対してオフセットを加算
                    var propPos = layer.property("Position");
                    if (!propPos) {
                        alert("Layer \"" + layer.name + "\" does not have a Position property.");
                        continue;
                    }
                    var layerBasePos = propPos.value; // CTI時点のPosition

                    // オフセット分を足したキーフレームを作成
                    var offsetKeyframes = [];
                    for (var u = 0; u < allKeyframes.length; u++) {
                        var offX = allKeyframes[u].value[0];
                        var offY = allKeyframes[u].value[1];
                        offsetKeyframes.push({
                            frame: allKeyframes[u].frame,
                            value: [layerBasePos[0] + offX, layerBasePos[1] + offY]
                        });
                    }
                    applyKeyframes(layer, "Position", offsetKeyframes);

                } else {
                    // Scale / Opacity / Rotation ⇒ 絶対値で適用
                    applyKeyframes(layer, selectedPropName, allKeyframes);
                }
            }

            app.endUndoGroup();
        };
    }

    //==================================================
    // 7) Basicウィンドウ (Scale, Shake, Opacity, Rotation)
    //==================================================
    function buildBasicWindow(thisObj) {
        var w = (thisObj instanceof Panel)
            ? thisObj
            : new Window("palette", "Basic Animations", undefined, { resizeable: true });

        var mainGroup = w.add("group");
        mainGroup.orientation = "column";
        mainGroup.alignChildren = ["fill", "top"];

        var rowGroup = mainGroup.add("group");
        rowGroup.orientation = "row";
        rowGroup.alignChildren = ["fill","top"];

        var leftGroup = rowGroup.add("group");
        leftGroup.orientation = "column";
        leftGroup.alignChildren = ["fill","top"];

        var rightGroup = rowGroup.add("group");
        rightGroup.orientation = "column";
        rightGroup.alignChildren = ["fill","top"];

        //--- Scale Panel
        var scalePanel = leftGroup.add("panel", undefined, "Scale");
        scalePanel.orientation = "column";

        var btnUp = scalePanel.add("button", undefined, "Scale Up");
        btnUp.onClick = function() {
            applyKeyframesFromSelection("Scale", [
                { frame: 0,  value: [0, 0] },
                { frame: 10, value: [100, 100] }
            ]);
        };
        var btnDown = scalePanel.add("button", undefined, "Scale Down");
        btnDown.onClick = function() {
            applyKeyframesFromSelection("Scale", [
                { frame: 0,  value: [100, 100] },
                { frame: 10, value: [0, 0] }
            ]);
        };

        //--- Shake Panel
        var shakePanel = leftGroup.add("panel", undefined, "Shake");
        shakePanel.orientation = "column";

        var btnH = shakePanel.add("button", undefined, "Horizontal Shake");
        btnH.onClick = function() {
            applyShakeKeyframesFromSelection(true);
        };
        var btnV = shakePanel.add("button", undefined, "Vertical Shake");
        btnV.onClick = function() {
            applyShakeKeyframesFromSelection(false);
        };

        //--- Opacity Panel
        var opacityPanel = rightGroup.add("panel", undefined, "Opacity");
        opacityPanel.orientation = "column";

        var btnFadeIn = opacityPanel.add("button", undefined, "Fade In");
        btnFadeIn.onClick = function() {
            applyKeyframesFromSelection("Opacity", [
                { frame: 0,  value: 0 },
                { frame: 10, value: 100 }
            ]);
        };
        var btnFadeOut = opacityPanel.add("button", undefined, "Fade Out");
        btnFadeOut.onClick = function() {
            applyKeyframesFromSelection("Opacity", [
                { frame: 0,  value: 100 },
                { frame: 10, value: 0 }
            ]);
        };

        var btnFadeIO = opacityPanel.add("button", undefined, "Fade In & Out");
        btnFadeIO.onClick = function() {
            applyKeyframesFromSelection("Opacity", [
                { frame: 0,  value: 0 },
                { frame: 10, value: 100 },
                { frame: 30, value: 100 },
                { frame: 40, value: 0 }
            ]);
        };

        var btnBlink = opacityPanel.add("button", undefined, "Blink");
        btnBlink.onClick = function() {
            applyKeyframesFromSelection("Opacity", [
                { frame: 0,  value: 100 },
                { frame: 5,  value: 50 },
                { frame: 10, value: 100 },
                { frame: 15, value: 50 },
                { frame: 20, value: 100 },
                { frame: 25, value: 50 },
                { frame: 30, value: 100 },
                { frame: 35, value: 50 }
            ]);
        };

        //--- Rotation Panel
        var rotationPanel = rightGroup.add("panel", undefined, "Rotation");
        rotationPanel.orientation = "column";

        var btnRotShake = rotationPanel.add("button", undefined, "Rotation Shake");
        btnRotShake.onClick = function() {
            applyRotationKeyframesFromSelection();
        };

        if (w instanceof Window) {
            w.center();
            w.show();
        } else {
            w.layout.layout(true);
        }

        return w;
    }

    //==================================================
    // 8) Customウィンドウ (Custom Keyframes, Repeated Keyframes)
    //==================================================
    function buildCustomWindow(thisObj) {
        var w = (thisObj instanceof Panel)
            ? thisObj
            : new Window("palette", "Custom Animations", undefined, { resizeable: true });

        var mainGroup = w.add("group");
        mainGroup.orientation = "column";
        mainGroup.alignChildren = ["fill","top"];

        // ① Custom Keyframes
        createCustomKeyframeUI(mainGroup);

        // ② Repeated Keyframes
        createRepeatedKeyframeUI(mainGroup);

        if (w instanceof Window) {
            w.center();
            w.show();
        } else {
            w.layout.layout(true);
        }
        return w;
    }

    //==================================================
    // 9) 実行: 2つのウィンドウを作る
    //==================================================
    var basicWin = buildBasicWindow(me);
    var customWin = buildCustomWindow(me);

    if (basicWin instanceof Window) {
        basicWin.show();
    }
    if (customWin instanceof Window) {
        customWin.show();
    }

})(this);