LoadActor("mods-taro.lua");
return Def.ActorFrame{
    Name= "xtl_actor_d",
    Def.Quad{
        Name= "xtl_actor_e",
        InitCommand= cmd(visible,false;sleep,9999),
    },
    Def.Quad{
        InitCommand=function(self)

            IsTSadded = false;
            BASE_BPM = 200;

            --[[
                SpeedModオプションの取得
                @args (プレイヤー 1P→0 2P→1)
            ]]
            function GetHiSpeed(player)
                local result = 1.0;
                local playerOption = GAMESTATE:GetPlayerState(player):GetPlayerOptionsString('ModsLevel_Song');
                -- SCREENMAN:SystemMessage(playerOption);
                local speedOption = string.match(playerOption, '(%d+%.%d+)x') == nil and
                    tonumber(string.match(playerOption, '(%d+)x')) or
                    tonumber(string.match(playerOption, '(%d+%.%d+)x'));

                if not speedOption then
                    speedOption = string.match(playerOption, '^C(%d+)') == nil and
                        tonumber(string.match(playerOption, '^m(%d+)')) or
                        tonumber(string.match(playerOption, '^C(%d+)'));

                    if speedOption then
                        result = speedOption / BASE_BPM;
                    end
                else
                    result = speedOption;
                end

                return result;
            end

            self:visible(false);
            self:queuecommand('getTS');
        end,
        getTSCommand=function(self)

            if TS and TS == SCREENMAN:GetTopScreen() then
                p1e = GAMESTATE:IsPlayerEnabled(0);
                p2e = GAMESTATE:IsPlayerEnabled(1);

                if p1e then
                    hispeedP1 = GetHiSpeed(0);
                end

                if p2e then
                    hispeedP2 = GetHiSpeed(1);
                end

                if not hispeedP1 then
                    hispeedP1 = 1;
                end
                if not hispeedP2 then
                    hispeedP2 = 1;
                end
                IsTSadded = true;
                taronuke_mods('*100000 '..tostring(hispeedP1)..'x', 1);
                taronuke_mods('*100000 '..tostring(hispeedP2)..'x', 2);
                MESSAGEMAN:Broadcast('gotTS');
            end

            TS = SCREENMAN:GetTopScreen();
            self:sleep(0.02);

            if not IsTSadded then
                self:queuecommand('getTS');
            end
        end,
    },

    Def.Quad{
        Name= "xtl_actor_f",
        gotTSMessageCommand=function(self)

            ------------------------
            -- Const
            ------------------------

            UPDATE = 'Update';

            ------------------------
            -- Function
            ------------------------
            --[[
                Rateオプションの値を取得
            ]]
            function getRateMod()
                local so = SCREENMAN:GetTopScreen():GetChild('SongOptions')
                local s = '';
                if not so then
                    return 1.0;
                else
                    s = so:GetText();
                end

                if not s then
                    return 1.0;
                end;

                local fbegin = string.find(s,'.',1,true);
                local fend = string.find(s,'xMusic');
                if fbegin and fend then
                    return tonumber(string.sub(s,fbegin-1,fend-1));
                else
                    return 1.0;
                end
            end

            --[[
                ノートの位置拍数を求める
                @args (小節,小節内の拍数,端数 1以下の値)
            ]]
            function calcBeat(measure, beat, fraction)
                return (measure-1) * 4.0 + beat + fraction;
            end

            --[[
                指定した拍数にかかる時間を求める
                @args (拍数, BPM)
            ]]
            function calcTime(beatLength, bpm)
                return beatLength * (60.0/bpm);
            end

            function getSpeedTime(beatLength)
                return 1.0/calcTime(beatLength, BASE_BPM);
            end

            function getTimeString(beatLength, bpm)
                return '*' .. tostring(1.0/calcTime(beatLength, bpm));
            end

            function getTimeString(beatLength)
                return '*' .. tostring(1.0/calcTime(beatLength, BASE_BPM));
            end

            --[[
                スピード変更Mod
                @args (変更速度, 値)
            ]]
            function ApplyXMod(velocity, value)
                taronuke_mods('*'..tostring(velocity*hfvP1)..' '..tostring(hispeedP1*value)..'x', 1);
                taronuke_mods('*'..tostring(velocity*hfvP2)..' '..tostring(hispeedP2*value)..'x', 2);
            end

            --[[
                フレーム待機
            ]]
            function WaitFrame()
                self:sleep(0.02);
            end

            --[[
                Modsを更新
                @args (Beat位置)
            ]]
            function UpdateMods(beat)

                -- Modループ
                for m, mod in ipairs(mods) do
                    if (beat >= mod[1] and lastBeat < mod[1]) then
                        taronuke_mods( mod[2], 1 );
                        taronuke_mods( mod[2], 2 );
                    elseif (beat < mod[1]) then
                        break
                    end
                end

                lastBeat = beat;

            end

            -----------------------
            -- Mod Event
            -----------------------
            -- Gimmik Mod
            mods = {
                {calcBeat(6, 2, 4/5), '*100000 50.0% stealth'},
                {calcBeat(6, 2, 4/5), '*100000 31.25% flip'},
                {calcBeat(6, 2, 4/5), '*100000 -18.75% invert'},
                {calcBeat(6, 2, 4/5), '*100000 -18.75% split'},
                {calcBeat(6, 2, 4/5), '*100000 18.75% alternate'},
                {calcBeat(6, 2, 4/5), '*100000 50% reverse'},
                {calcBeat(7, 2, 2/5), getTimeString(4) .. ' 0% flip'},
                {calcBeat(7, 2, 2/5), getTimeString(4) .. ' 0% invert'},
                {calcBeat(7, 2, 2/5), getTimeString(4) .. ' 0% split'},
                {calcBeat(7, 2, 2/5), getTimeString(4) .. ' 0% alternate'},
                {calcBeat(7, 2, 2/5), getTimeString(4) .. ' 0% reverse'},
                {calcBeat(7, 2, 2/5), getTimeString(8) .. ' no stealth'},
                {calcBeat(281, 0, 0), getTimeString(4) .. ' 50.0% stealth'},
                {calcBeat(281, 0, 0), getTimeString(0.5) .. ' 90% reverse'},
                {calcBeat(281, 0, 1/2), getTimeString(0.5) .. ' 10% reverse'},
                {calcBeat(281, 1, 0), getTimeString(0.5) .. ' 80% reverse'},
                {calcBeat(281, 1, 1/2), getTimeString(0.5) .. ' 20% reverse'},
                {calcBeat(281, 2, 0), getTimeString(0.5) .. ' 70% reverse'},
                {calcBeat(281, 2, 1/2), getTimeString(0.5) .. ' 30% reverse'},
                {calcBeat(281, 3, 0), getTimeString(0.5) .. ' 60% reverse'},
                {calcBeat(281, 3, 1/2), getTimeString(0.5) .. ' 40% reverse'},
                {calcBeat(282, 0, 0), getTimeString(0.5) .. ' 50% reverse'},
                {calcBeat(282, 0, 0), getTimeString(0.5) .. ' 50.0% stealth'},
                {calcBeat(282, 0, 0), getTimeString(0.5) .. ' 31.25% flip'},
                {calcBeat(282, 0, 0), getTimeString(0.5) .. ' -18.75% invert'},
                {calcBeat(282, 0, 0), getTimeString(0.5) .. ' -18.75% split'},
                {calcBeat(282, 0, 0), getTimeString(0.5) .. ' 18.75% alternate'},
                {calcBeat(282, 0, 0), getTimeString(8) .. ' 100% dark'}
            }

            -- SpeedMod
            speedMods = {
            }

            -----------------------
            -- Cache
            -----------------------
            isPlayerEntry = {};
            isPlayerEntry[0] = (GAMESTATE:IsPlayerEnabled(0) == true);
            isPlayerEntry[1] = (GAMESTATE:IsPlayerEnabled(1) == true);

            lastBeat = -16.0;
            -- hispeedP1 = isPlayerEntry[0] and getPlayerHiSpeed(0) or 1;
            -- hispeedP2 = isPlayerEntry[1] and getPlayerHiSpeed(1) or 1;

            hfvP1 = hispeedP1 / getRateMod();
            hfvP2 = hispeedP2 / getRateMod();
            -----------------------

            self:sleep(1);
            self:queuecommand( UPDATE );
        end,
        UpdateCommand= function(self)
            beat = GAMESTATE:GetSongBeat();
            UpdateMods(beat);
            WaitFrame();
            self:queuecommand( UPDATE );
        end,
    },
}
