using Jint.Native;

namespace Jint.Runtime.Debugger
{
    public sealed class DebugInformation : EventArgs
    {
        private readonly Engine _engine;
        private readonly SourceLocation _currentLocation;
        private readonly JsValue? _returnValue;

        private DebugCallStack? _callStack;

        internal DebugInformation(
            Engine engine,
            Node? currentNode,
            in SourceLocation currentLocation,
            JsValue? returnValue,
            long currentMemoryUsage,
            PauseType pauseType,
            BreakPoint? breakPoint)
        {
            _engine = engine;
            CurrentNode = currentNode;
            _currentLocation = currentLocation;
            _returnValue = returnValue;
            CurrentMemoryUsage = currentMemoryUsage;
            PauseType = pauseType;
            BreakPoint = breakPoint;
        }

        /// <summary>
        /// Indicates the type of pause that resulted in this DebugInformation being generated.
        /// </summary>
        public PauseType PauseType { get; }

        /// <summary>
        /// Breakpoint at the current location. This will be set even if the pause wasn't caused by the breakpoint.
        /// </summary>
        public BreakPoint? BreakPoint { get; }

        /// <summary>
        /// The current call stack.
        /// </summary>
        /// <remarks>This will always include at least a call frame for the global environment.</remarks>
        public DebugCallStack CallStack =>
            _callStack ??= new DebugCallStack(_engine, _currentLocation, _engine.CallStack, _returnValue);

        /// <summary>
        /// The AST Node that will be executed on next step.
        /// Note that this will be null when execution is at a return point.
        /// </summary>
        public Node? CurrentNode { get; }

        /// <summary>
        /// The current source Location.
        /// For return points, this starts and ends at the end of the function body.
        /// </summary>
        public SourceLocation Location => CurrentCallFrame.Location;

        /// <summary>
        /// Not implemented. Will always return 0.
        /// </summary>
        public long CurrentMemoryUsage { get; }

        /// <summary>
        /// The currently executing call frame.
        /// </summary>
        public CallFrame CurrentCallFrame => CallStack[0];

        /// <summary>
        /// The scope chain of the currently executing call frame.
        /// </summary>
        public DebugScopes CurrentScopeChain => CurrentCallFrame.ScopeChain;

        /// <summary>
        /// The return value of the currently executing call frame.
        /// This is null if execution is not at a return point.
        /// </summary>
        public JsValue? ReturnValue => CurrentCallFrame.ReturnValue;
    }
}
