(function (me) {

    //ウィンドウを保持するグローバル配列を用意
    var customWindows = [];

    //==================================================
    // 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);
        }
    }

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

        var subGroup1 = panel.add("group");
        subGroup1.orientation = "row";
        subGroup1.alignChildren = ["fill", "center"];

        var subGroup2 = panel.add("group");
        subGroup2.orientation = "row";
        subGroup2.alignChildren = ["fill", "center"];

        var subGroup3 = panel.add("group");
        subGroup3.orientation = "row";
        subGroup3.alignChildren = ["fill", "center"];

        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"
            }
        };

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

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

        subGroup3.add("statictext", undefined, "Values (comma separated):");
        var valueInput = subGroup3.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();
        };
    }

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

        var subGroup1 = panel.add("group");
        subGroup1.orientation = "row";
        subGroup1.alignChildren = ["fill", "center"];

        var subGroup2 = panel.add("group");
        subGroup2.orientation = "row";
        subGroup2.alignChildren = ["fill", "center"];

        var subGroup3 = panel.add("group");
        subGroup3.orientation = "row";
        subGroup3.alignChildren = ["fill", "center"];

        var subGroup4 = panel.add("group");
        subGroup4.orientation = "row";
        subGroup4.alignChildren = ["fill", "center"];

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

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

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

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

        subGroup4.add("statictext", undefined, "Repeat count:");
        var repeatInput = subGroup4.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();
        };
    }

    //==================================================
    // ) Customウィンドウ (Custom Keyframes, Repeated Keyframes)
    //==================================================

    function buildCustomKeyframeWindow() {
        var w = new Window("palette", "Custom Animations", undefined, { resizeable: true });

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

        // ① Custom Keyframes
        createCustomKeyframeUI(mainGroup);

        if (!(w instanceof Window)) {
            w.layout.layout(true);
        }

        return w;
    }

    function buildRepeatedKeyframeWindow() {
        var w = new Window("palette", "Custom Animations", undefined, { resizeable: true });

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

        // ② Repeated Keyframes
        createRepeatedKeyframeUI(mainGroup);

        if (!(w instanceof Window)) {
            w.layout.layout(true);
        }

        return w;
    }

    //==================================================
    // ) Basicウィンドウ
    //==================================================
    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"];

        //  カスタムウィンドウ(Custom Keyframes)を開くボタンを追加
        var openCustomButton = mainGroup.add("button", undefined, "Add Custom Keyframes Window");

        //  クリックするたびに新しいウィンドウを開く
        openCustomButton.onClick = function () {

            var newWin = buildCustomKeyframeWindow();
            customWindows.push(newWin);
            newWin.onClose = function () {
                var index = customWindows.indexOf(newWin);
                if (index !== -1) customWindows.splice(index, 1);
            };
            newWin.show();
        };

        //  カスタムウィンドウ(Repeat Keyframes)を開くボタンを追加
        var openCustomButton2 = mainGroup.add("button", undefined, "Add Repeat Keyframes Window");

        //  クリックするたびに新しいウィンドウを開く
        openCustomButton2.onClick = function () {

            var newWin = buildRepeatedKeyframeWindow();
            customWindows.push(newWin);
            newWin.onClose = function () {
                var index = customWindows.indexOf(newWin);
                if (index !== -1) customWindows.splice(index, 1);
            };
            newWin.show();
        };

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

        return w;
    }

    //==================================================
    // ) 実行: ウィンドウを作る
    //==================================================
    var basicWin = buildBasicWindow(me);

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

})(this);