using NUnit.Framework.Internal;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;

public class ScriptVM : MonoBehaviour
{
    private Script _currentScript;
    private ExecutionContext _mainContext;
    private Dictionary<int, ExecutionContext> _activeSubroutines = new Dictionary<int, ExecutionContext>();
    private Dictionary<int, GameObject> _modelTempRegistry = new Dictionary<int, GameObject>();
    private Dictionary<int, GameObject> _objectRegistry = new Dictionary<int, GameObject>();

    private int selectionVariable;

    // Jump Table обработчиков команд
    private Dictionary<uint, Action<ExecutionContext, ScriptCommand>> _opCodeHandlers;

    public bool IsRunning => _mainContext != null && !_mainContext.IsFinished;

    //SCRIPT LOGGING
    void OnGUI()
    {
        GUILayout.BeginArea(new Rect(10, 10, 400, 600));

        if (_mainContext != null)
        {
            GUILayout.Label($"MAIN: PC={_mainContext.ProgramCounter:X4} CMD=0x{GetCurrentCommand(_mainContext)?.OpCode:X4}");
            GUILayout.Label($"Status: {(IsRunning ? "Running" : "Stopped")}");
            GUILayout.Label($"Paused: {(_mainContext.IsPaused ? "+" : "-")} RemSec: -{_mainContext.PauseTimeRemaining}");

            // Текущие параметры команды
            var currentCmd = GetCurrentCommand(_mainContext);
            if (currentCmd != null)
            {
                GUILayout.Label($"Params: {string.Join(", ", currentCmd.Params)}");
            }
        }

        // Подпрограммы
        GUILayout.Label($"Subroutines: {_activeSubroutines.Count}");
        foreach (var kvp in _activeSubroutines)
        {
            var context = kvp.Value;
            GUILayout.Label($"SUB[{kvp.Key:X2}] {context.ContextName}: PC={context.ProgramCounter:X4} CMD=0x{GetCurrentCommand(context)?.OpCode:X4}");
            GUILayout.Label($"  Paused: {(context.IsPaused ? "+" : "-")} RemSec: -{context.PauseTimeRemaining}");
        }

        GUILayout.EndArea();
    }

    void Awake()
    {
        InitializeJumpTable();
    }

    void Update()
    {
        if (_mainContext == null) return;

        if (_mainContext.IsPaused && _mainContext.PauseTimeRemaining > 0)
        {
            _mainContext.PauseTimeRemaining -= Time.deltaTime;
            if (_mainContext.PauseTimeRemaining <= 0)
            {
                _mainContext.PauseTimeRemaining = 0;
                _mainContext.IsPaused = false;
                _mainContext.ProgramCounter++;
                MarkCurrentCommandCompleted(_mainContext); // Помечаем текущую команду как выполненную
            }
        }

        // Выполняем основной контекст
        if (!_mainContext.IsWaiting && !_mainContext.IsPaused)
        {
            ExecuteStep(_mainContext);
        }

        // Обрабатываем паузу в подпрограммах
        foreach (var context in _activeSubroutines.Values)
        {
            if (context.IsPaused && context.PauseTimeRemaining > 0)
            {
                context.PauseTimeRemaining -= Time.deltaTime;
                if (context.PauseTimeRemaining <= 0)
                {
                    context.PauseTimeRemaining = 0;
                    context.IsPaused = false;
                    context.ProgramCounter++;
                    MarkCurrentCommandCompleted(context);
                }
            }
        }


        // Выполняем все активные подпрограммы
        foreach (var context in _activeSubroutines.Values)
        {
            if (!context.IsWaiting && !context.IsPaused)
            {
                ExecuteStep(context);
            }
        }

        // Удаляем завершенные подпрограммы
        _activeSubroutines = _activeSubroutines.Where(kvp => !kvp.Value.IsFinished).ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
    }

    public void LoadAndRunScript(Script script)
    {
        _currentScript = script;
        _mainContext = new ExecutionContext(0, "MAIN", null);
        _activeSubroutines.Clear();
        _objectRegistry.Clear();

        Debug.Log($"Script loaded with {script.Commands.Count} commands");
    }

    public void StartSubroutine(ushort subId)
    {
        if (_currentScript != null && _currentScript.SubroutineTable.TryGetValue(subId, out Subroutine sub))
        {
            var newContext = new ExecutionContext(sub.Address, sub.Name, new ScriptCommand {OpCode = 999});
            _activeSubroutines[subId] = newContext;
            Debug.Log($"Started independent subroutine {subId} at address {sub.Address:X4}");
        }
        else
        {
            Debug.LogWarning($"Cannot start subroutine {subId}: script not loaded or subroutine not found");
        }
    }

    private bool IsCommandCompleted(uint commandId)
    {
        if (commandId < _currentScript.Commands.Count)
        {
            return _currentScript.Commands[(int)commandId].executedStatus == 3;
        }
        return false;
    }

    // WaitTo - stops thread and check command status at address
    private void Handle_WaitTo(ExecutionContext ctx, ScriptCommand cmd)
    {
        uint targetCommandId = (uint)GetParam(cmd, 0);

        if (IsCommandCompleted(targetCommandId))
        {
            ctx.ProgramCounter++;
            cmd.executedStatus = 3;
        }

    }

    private void ExecuteStep(ExecutionContext context)
    {
        if (context.ProgramCounter >= _currentScript.Commands.Count)
        {
            context.IsFinished = true;
            if (context == _mainContext) Debug.Log("Main script finished");
            return;
        }

        ScriptCommand cmd = _currentScript.Commands[(int)context.ProgramCounter];
        cmd.executedStatus = 0;                                             //cleaning status for command restart and proper wait
        if (_opCodeHandlers.TryGetValue(cmd.OpCode, out var handler))
        {
            try
            {
                handler(context, cmd);
            }
            catch (Exception ex)
            {
                Debug.LogError($"Error executing command 0x{cmd.OpCode:X8}: {ex.Message}");
                // При ошибке все равно помечаем как выполненную, чтобы не зависнуть
                cmd.executedStatus = 3;
                context.ProgramCounter++;
            }
        }
        else
        {
            Debug.LogWarning($"Unknown OpCode: 0x{cmd.OpCode:X8} at PC={context.ProgramCounter:X4}");
            // Неизвестные команды просто пропускаем
            cmd.executedStatus = 3;
            context.ProgramCounter++;
        }
    }

    private void InitializeJumpTable()
    {
        _opCodeHandlers = new Dictionary<uint, Action<ExecutionContext, ScriptCommand>>
        {
            // Управление потоком
            { 0x00000007, Handle_JumpTo },
            { 0x00000001, Handle_If1Go },
            { 0x00000002, Handle_If0Go },
            { 0x00000004, Handle_IfNotEqualGo },
            { 0x0000000D, Handle_GoSub },
            { 0x0000000E, Handle_Return },
            { 0x00000012, Handle_WaitTo },

            { 0x0000001A, Handle_GetInput },

            { 0x0000002C, Handle_SetCamXY },   // Set Camera

            //Characters commands
            { 0x00000030, Handle_CharLoad },   // CharLoad
            { 0x00000031, Handle_CharInit },   // CharInit
            { 0x000000D0, Handle_CharInit },   // Another CharInit???
            { 0x00000034, Handle_CharMoveTo },   // CanWait
            { 0x00000037, Handle_CharAnimate },   // CanWait
            { 0x00000038, Handle_CharAnimate2 },   // CanWait

            { 0x00000041, Handle_PlayerInit },   // Create player char at camera set
            
            { 0x000000D2, Handle_CharGlowShow },   // CharInitWithGlow

            { 0x0000003E, Handle_WaitCharAnimEnd },   // Wait by char id

            { 0x00000046, Handle_CharSetSubroutine },   // Set blockcharacter subroutine


            //Pause
            { 0x0000005C, Handle_PausInit },   // PausInit
            { 0x0000005D, Handle_PauseFr },    // PauseFr_
            
            // Работа с переменными
            { 0x00000008, Handle_SetTemp },
            { 0x00000009, Handle_CopyVar },
            { 0x00000060, Handle_GetVar },
            { 0x0000005E, Handle_VarTrue },
            { 0x0000005F, Handle_VarFlse },
            { 0x00000073, Handle_VarDec },
            { 0x00000074, Handle_VarSet },
            
            // Управление
            { 0x00000043, Handle_CtrlLock },
            { 0x00000044, Handle_CtrUnlck },
            
            // Загрузка модулей
            { 0x00000051, Handle_DungLoad },
            { 0x00000052, Handle_EventLoad },
            { 0x00000053, Handle_CityLoad },
            { 0x000000ED, Handle_BattleLoad },

            
            // Видео
            { 0x00000059, Handle_VideoPly },
            
            // Эффекты
            { 0x00000092, Handle_ScrnFade },

            // Текст

            { 0x00000024, Handle_WindOpen },
            { 0x00000013, Handle_TextShow },
            { 0x00000025, Handle_WindClose }
        };
    }

    // ========== ОБРАБОТЧИКИ КОМАНД ==========

    private void Handle_JumpTo(ExecutionContext ctx, ScriptCommand cmd)
    {
        uint jumpAddress = (uint)GetParam(cmd, 0);
        ctx.ProgramCounter = jumpAddress;
        cmd.executedStatus = 3; // Немедленное выполнение
    }

    private void Handle_If1Go(ExecutionContext ctx, ScriptCommand cmd)
    {
        int varAddress = GetParam(cmd, 0);
        uint jumpAddress = (uint)GetParam(cmd, 1);

        if (GetLocalVariable(ctx, varAddress) == 1)
            ctx.ProgramCounter = jumpAddress;
        else
            ctx.ProgramCounter++;
        cmd.executedStatus = 3;
    }

    private void Handle_If0Go(ExecutionContext ctx, ScriptCommand cmd)
    {
        int varAddress = GetParam(cmd, 0);
        uint jumpAddress = (uint)GetParam(cmd, 1);

        if (GetLocalVariable(ctx, varAddress) == 0)
            ctx.ProgramCounter = jumpAddress;
        else
            ctx.ProgramCounter++;
        cmd.executedStatus = 3;
    }

    private void Handle_IfNotEqualGo(ExecutionContext ctx, ScriptCommand cmd)
    {
        int varAddress = GetParam(cmd, 0);
        int compareValue = GetParam(cmd, 1);
        uint jumpAddress = (uint)GetParam(cmd, 2);

        if (GetLocalVariable(ctx, varAddress) != compareValue)
            ctx.ProgramCounter = jumpAddress;
        else
            ctx.ProgramCounter++;
        cmd.executedStatus = 3;
    }

    private void Handle_GoSub(ExecutionContext ctx, ScriptCommand cmd)
    {
        ushort subId = (ushort)GetParam(cmd, 0);

        if (_currentScript.SubroutineTable.TryGetValue(subId, out Subroutine sub))
        {
            var newContext = new ExecutionContext(sub.Address, sub.Name, cmd);
            _activeSubroutines[subId] = newContext;
        }
        ctx.ProgramCounter++;
    }

    private void Handle_Return(ExecutionContext ctx, ScriptCommand cmd)
    {
        if (ctx.ExecutedByCmd != null)  //main thread has ExecutedByCmd = null
        {
            // Для подпрограмм, вызванных из основного потока
            ctx.ExecutedByCmd.executedStatus = 3;
            ctx.IsFinished = true;
        }

    }
    private void Handle_GetInput(ExecutionContext ctx, ScriptCommand cmd)
    {
        int varAddress = GetParam(cmd, 0);
        SetLocalVariable(ctx, varAddress, selectionVariable);
        ctx.ProgramCounter++;
        cmd.executedStatus = 3;
    }


    // Обработчики команд паузы:
    private void Handle_PausInit(ExecutionContext ctx, ScriptCommand cmd)
    {
        // Просто сбрасываем счётчик паузы в контексте
        ctx.PauseTimeRemaining = 0;
        ctx.ProgramCounter++;
        cmd.executedStatus = 3;
    }

    private void Handle_PauseFr(ExecutionContext ctx, ScriptCommand cmd)
    {
        int framesToPause = GetParam(cmd, 0);

        ctx.PauseTimeRemaining = ctx.PauseTimeRemaining = framesToPause * (1f / App.Ps1FPS); //Ps1 FPS Compability
        ctx.IsPaused = true; // Ставим контекст на паузу
    }

    private void Handle_SetTemp(ExecutionContext ctx, ScriptCommand cmd)
    {
        int varAddress = GetParam(cmd, 0);
        int value = GetParam(cmd, 1);
        SetLocalVariable(ctx, varAddress, value);
        ctx.ProgramCounter++;
        cmd.executedStatus = 3;
    }

    private void Handle_CopyVar(ExecutionContext ctx, ScriptCommand cmd)
    {
        int srcAddress = GetParam(cmd, 0);
        int dstAddress = GetParam(cmd, 1);
        int value = GetLocalVariable(ctx, srcAddress);
        SetLocalVariable(ctx, dstAddress, value);
        ctx.ProgramCounter++;
        cmd.executedStatus = 3;
    }

    private void Handle_GetVar(ExecutionContext ctx, ScriptCommand cmd)
    {
        int localVarAddress = GetParam(cmd, 0);
        int globalVarAddress = GetParam(cmd, 1);
        int value = App.GameState.GetGlobalVariable(globalVarAddress);
        SetLocalVariable(ctx, localVarAddress, value);
        ctx.ProgramCounter++;
        cmd.executedStatus = 3;
    }

    private void Handle_VarTrue(ExecutionContext ctx, ScriptCommand cmd)
    {
        int globalVarAddress = GetParam(cmd, 0);
        App.GameState.SetGlobalVariable(globalVarAddress, 1);
        ctx.ProgramCounter++;
        cmd.executedStatus = 3;
    }

    private void Handle_VarFlse(ExecutionContext ctx, ScriptCommand cmd)
    {
        int globalVarAddress = GetParam(cmd, 0);
        App.GameState.SetGlobalVariable(globalVarAddress, 0);
        ctx.ProgramCounter++;
        cmd.executedStatus = 3;
    }

    private void Handle_VarSet(ExecutionContext ctx, ScriptCommand cmd)
    {
        int globalVarAddress = GetParam(cmd, 0);
        int value = GetParam(cmd, 1);
        App.GameState.SetGlobalVariable(globalVarAddress, value);
        ctx.ProgramCounter++;
        cmd.executedStatus = 3;
    }

    private void Handle_VarDec(ExecutionContext ctx, ScriptCommand cmd)
    {
        int globalVarAddress = GetParam(cmd, 0);
        int currentValue = App.GameState.GetGlobalVariable(globalVarAddress);
        App.GameState.SetGlobalVariable(globalVarAddress, currentValue - 1);
        ctx.ProgramCounter++;
        cmd.executedStatus = 3;
    }

    private void Handle_CtrlLock(ExecutionContext ctx, ScriptCommand cmd)
    {
        Debug.Log("Controls Locked");
        App.GameState.IsPlayerControlLocked = true;
        ctx.ProgramCounter++;
        cmd.executedStatus = 3;
    }

    private void Handle_CtrUnlck(ExecutionContext ctx, ScriptCommand cmd)
    {
        Debug.Log("Controls Unlocked");
        App.GameState.IsPlayerControlLocked = false;

        if (_objectRegistry.TryGetValue(1, out GameObject playerObj))
        {
            ControlsController.Instance?.SetControlledObject(playerObj);
        }

        ctx.ProgramCounter++;
        cmd.executedStatus = 3;
    }

    private void Handle_SetCamXY(ExecutionContext ctx, ScriptCommand cmd)
    {
        uint rot = (uint)GetParam(cmd, 0);
        uint x = (uint)GetParam(cmd, 1);
        uint y = (uint)GetParam(cmd, 2);
        uint z = (uint)GetParam(cmd, 3);

        App.CameraController.SetCamTargetXY(rot, x, y, z);
        Debug.Log($"Camera position set {x}.{y}.{z}. Rot - {rot}");
        ctx.ProgramCounter++;
        cmd.executedStatus = 3;
    }

    private void Handle_TextShow(ExecutionContext ctx, ScriptCommand cmd)
    {
        int textOffset = GetParam(cmd, 0);

        var textTokens = TextUtils.ParseText(_currentScript.TextData, textOffset);

        // Передаем команду в UI менеджер
        App.TextManager.ShowText(textTokens, cmd);

        ctx.ProgramCounter++;
    }

    private void Handle_WindOpen(ExecutionContext ctx, ScriptCommand cmd)
    {
        App.UI.ShowMessageWindow();
        ctx.ProgramCounter++;
        cmd.executedStatus = 3;
    }

    private void Handle_WindClose(ExecutionContext ctx, ScriptCommand cmd)
    {
        App.UI.HideMessageWindow(ctx);
        ctx.ProgramCounter++;
        cmd.executedStatus = 3;
    }

    //CHARACTERS SECTION

    private void Handle_CharLoad(ExecutionContext ctx, ScriptCommand cmd)
    {
        //Old PS1 method for loading sprite sheet in VRAM
        //Now we simply creating disabled char model on scene
        int modelId = GetParam(cmd, 0);
        if (!_modelTempRegistry.ContainsKey(modelId))
        {
            GameObject model = App.ResourceManager.CharLoad(modelId);
            _modelTempRegistry[modelId] = model;
        }

        ctx.ProgramCounter++;
        cmd.executedStatus = 3;
    }

    private void Handle_CharInit(ExecutionContext ctx, ScriptCommand cmd)
    {
        // Параметры: id, model_id, x, y, z, turn, dummy
        int objectId = GetParam(cmd, 0);
        int modelId = GetParam(cmd, 1);
        uint posX = (uint)GetParam(cmd, 2);
        uint posY = (uint)GetParam(cmd, 3);
        uint posZ = (uint)GetParam(cmd, 4);
        uint rotation = (uint)GetParam(cmd, 5);
        int dummy = GetParam(cmd, 6); // Неиспользуемый параметр

        if (!_modelTempRegistry.TryGetValue(modelId, out GameObject templateModel))
        {
            Debug.LogWarning($"Model {modelId} not loaded, creating...");
            templateModel = App.ResourceManager.CharLoad(modelId);
            _modelTempRegistry[modelId] = templateModel;
        }

        if (_objectRegistry.TryGetValue(objectId, out GameObject existingModel))
        {
            Destroy(existingModel);
            _objectRegistry.Remove(objectId);
        }

        GameObject characterInstance = App.ResourceManager.CharInit(templateModel, objectId, posX, posY, posZ, rotation);

        _objectRegistry[objectId] = characterInstance;

        ctx.ProgramCounter++;
        cmd.executedStatus = 3;
    }

        // Activate player object at camera spot position and link controls
    private void Handle_PlayerInit(ExecutionContext ctx, ScriptCommand cmd)
    {
        int rotation = GetParam(cmd, 0);

        if (!_modelTempRegistry.TryGetValue(1, out GameObject templateModel))
        {
            Debug.LogWarning($"Player Model not loaded, creating...");
            templateModel = App.ResourceManager.CharLoad(1);
            _modelTempRegistry[1] = templateModel;
        }

        if (_objectRegistry.TryGetValue(1, out GameObject existingModel))
        {
            Destroy(existingModel);
            _objectRegistry.Remove(1);
        }

        CameraController sceneCamera = App.CameraController;
        GameObject character = App.ResourceManager.CharInit(templateModel, 1, sceneCamera.sourceX, sceneCamera.sourceY, sceneCamera.sourceZ, (uint)rotation);

        _objectRegistry[1] = character;

        ctx.ProgramCounter++;
        cmd.executedStatus = 3;
    }

    private void Handle_CharGlowShow(ExecutionContext ctx, ScriptCommand cmd)
    {
        // Параметры: id, x, y, z, glow, dummy
        int charId = GetParam(cmd, 0);
        uint posX = (uint)GetParam(cmd, 1);
        uint posY = (uint)GetParam(cmd, 2);
        uint posZ = (uint)GetParam(cmd, 3);
        uint glow = (uint)GetParam(cmd, 4);
        int dummy = GetParam(cmd, 5);

        if (_objectRegistry.TryGetValue(charId, out GameObject characterObj))
        {

          App.ResourceManager.CharInit(characterObj, charId, posX, posY, posZ, glow);           //TODO: Async GLOW
        }

        ctx.ProgramCounter++;
        cmd.executedStatus = 3;
    }

    private void Handle_CharMoveTo(ExecutionContext ctx, ScriptCommand cmd)
    {
        int charId = GetParam(cmd, 0);
        uint destX = (uint)GetParam(cmd, 1);
        uint destZ = (uint)GetParam(cmd, 2);
        uint endAction = (uint)GetParam(cmd, 3);
        uint speed = (uint)GetParam(cmd, 4);

        if (_objectRegistry.TryGetValue(charId, out GameObject characterObj) && characterObj is GameObject character)
        {
            var controller = character.GetComponent<CharacterController>();
            if (controller != null)
            {
                //async move
                controller.MoveToGameCoordinates(destX, destZ, endAction, speed);
            }
        }

        ctx.ProgramCounter++;
        cmd.executedStatus = 3;
    }

    private void Handle_CharAnimate(ExecutionContext ctx, ScriptCommand cmd)
    {
        int charId = GetParam(cmd, 0);
        uint animationId = (uint)GetParam(cmd, 1);
        uint repeatNum = (uint)GetParam(cmd, 2);

        if (_objectRegistry.TryGetValue(charId, out GameObject characterObj) && characterObj is GameObject character)
        {
            var controller = character.GetComponent<CharacterController>();
            if (controller != null)
            {
                controller.PlayAnimation((int)animationId, (int)Mathf.Max(1, repeatNum));
            }
        }

        ctx.ProgramCounter++;
        cmd.executedStatus = 3;
    }

    private void Handle_CharAnimate2(ExecutionContext ctx, ScriptCommand cmd)
    {
        Handle_CharAnimate(ctx, cmd);
    }
    
    private void Handle_WaitCharAnimEnd(ExecutionContext ctx, ScriptCommand cmd)
    {
        int charId = GetParam(cmd, 0);

        if (_objectRegistry.TryGetValue(charId, out GameObject characterObj) && characterObj is GameObject character)
        {
            var controller = character.GetComponent<CharacterController>();
            if (controller != null && !controller.IsMoving)
            {
                ctx.ProgramCounter++;
                cmd.executedStatus = 3;
            }
        }
        else
        {
            ctx.ProgramCounter++;
            cmd.executedStatus = 3;
        }
    }

    private void Handle_CharSetSubroutine(ExecutionContext ctx, ScriptCommand cmd)
    {
        int charId = GetParam(cmd, 0);
        int subId = GetParam(cmd, 1);

        _objectRegistry.TryGetValue(charId, out GameObject characterObj);
        characterObj.GetComponent<CharacterController>().blockSubroutine = subId;
        Debug.Log($"Sub {subId} set to char {charId}");
        ctx.ProgramCounter++;
        cmd.executedStatus = 3;
    }


    // SCENERY LOADERS
    private void Handle_CityLoad(ExecutionContext ctx, ScriptCommand cmd)
    {
        int districtId = GetParam(cmd, 0);
        int y = GetParam(cmd, 1);
        int x = GetParam(cmd, 2);
        App.SceneLoader.LoadCity(districtId, y, x);
        _mainContext = null; // Завершаем текущий скрипт
    }
    private void Handle_BattleLoad(ExecutionContext ctx, ScriptCommand cmd)
    {
        int battleId = GetParam(cmd, 0);
        App.SceneLoader.LoadBattle(battleId);
        _mainContext = null; // Завершаем текущий скрипт
    }
    private void Handle_DungLoad(ExecutionContext ctx, ScriptCommand cmd)
    {
        int dungeonId = GetParam(cmd, 0);
        App.SceneLoader.LoadDungeon(dungeonId);
        _mainContext = null; // Завершаем текущий скрипт
    }
    private void Handle_EventLoad(ExecutionContext ctx, ScriptCommand cmd)
    {
        int hzId = GetParam(cmd, 0);
        int eventId = GetParam(cmd, 1);
        App.SceneLoader.LoadEvent(eventId, hzId);
        _mainContext = null; // Завершаем текущий скрипт
    }

    private void Handle_VideoPly(ExecutionContext ctx, ScriptCommand cmd)
    {
        int videoId = GetParam(cmd, 0);
        App.VideoPlayer.PlayVideo(videoId, cmd);
        PauseContext(cmd);
    }

    private void Handle_ScrnFade(ExecutionContext ctx, ScriptCommand cmd)
    {
        int fadeType = GetParam(cmd, 0);
        int duration = GetParam(cmd, 1);
        //App.UI.ScreenFade(fadeType, duration, () => {
        //    ctx.ProgramCounter++;
        //});
        ctx.ProgramCounter++;
    }

    public void PauseContext(ScriptCommand cmd)
    {
        // Находим контекст, который выполняет эту команду
        if (_mainContext != null && GetCurrentCommand(_mainContext) == cmd)
        {
            _mainContext.IsPaused = true;
            return;
        }

        foreach (var context in _activeSubroutines.Values)
        {
            if (GetCurrentCommand(context) == cmd)
            {
                context.IsPaused = true;
                return;
            }
        }
    }

    public void ResumeContext(ScriptCommand cmd)
    {
        // Находим контекст и снимаем паузу
        if (_mainContext != null && GetCurrentCommand(_mainContext) == cmd)
        {
            _mainContext.IsPaused = false;
            _mainContext.ProgramCounter++; // Переходим к следующей команде
            return;
        }

        foreach (var context in _activeSubroutines.Values)
        {
            if (GetCurrentCommand(context) == cmd)
            {
                context.IsPaused = false;
                context.ProgramCounter++; // Переходим к следующей команде
                return;
            }
        }
    }

    // ========== ВСПОМОГАТЕЛЬНЫЕ МЕТОДЫ ==========

    private ScriptCommand GetCurrentCommand(ExecutionContext ctx)
    {
        if (ctx.ProgramCounter < _currentScript.Commands.Count)
            return _currentScript.Commands[(int)ctx.ProgramCounter];
        return null;
    }

    private int GetParam(ScriptCommand cmd, int index)
    {
        return index < cmd.Params.Length ? cmd.Params[index] : 0;
    }

    private int GetLocalVariable(ExecutionContext ctx, int address)
    {
        return ctx.LocalVariables.TryGetValue(address, out int value) ? value : 0;
    }

    private void SetLocalVariable(ExecutionContext ctx, int address, int value)
    {
        ctx.LocalVariables[address] = value;
    }


    public void SetSelectionVariable(int value)     //external after selection variable set
    {
        selectionVariable = value;
    }

    public void MarkCommandCompleted(ScriptCommand cmd)
    {
        cmd.executedStatus = 3;
        ResumeContext(cmd);
    }

    private void MarkCurrentCommandCompleted(ExecutionContext ctx)
    {
        if (ctx.ProgramCounter > 0 && ctx.ProgramCounter - 1 < _currentScript.Commands.Count)
        {
            ScriptCommand cmd = _currentScript.Commands[(int)(ctx.ProgramCounter - 1)];
            cmd.executedStatus = 3;
        }
    }
}

public class ExecutionContext
{
    public ScriptCommand ExecutedByCmd { get; set; }
    public string ContextName { get; set; }
    public uint ProgramCounter { get; set; }
    public Dictionary<int, int> LocalVariables { get; set; }
    public bool IsWaiting { get; set; }
    public bool IsPaused { get; set; }
    public bool IsFinished { get; set; }
    public float PauseTimeRemaining { get; set; }

    public ExecutionContext(uint startAddress, string name, ScriptCommand cmd)
    {
        ContextName = name;
        ExecutedByCmd = cmd;
        ProgramCounter = startAddress;
        LocalVariables = new Dictionary<int, int>();
        PauseTimeRemaining = 0;
    }
}