/*
 * EngineContext.cs --
 *
 * Copyright (c) 2007-2012 by Joe Mistachkin.  All rights reserved.
 *
 * See the file "license.terms" for information on usage and redistribution of
 * this file, and for a DISCLAIMER OF ALL WARRANTIES.
 *
 * RCS: @(#) $Id: $
 */

using System;
using Eagle._Attributes;
using Eagle._Components.Public;

#if DEBUGGER
using Eagle._Components.Public.Delegates;
#endif

using Eagle._Containers.Private;
using Eagle._Containers.Public;
using Eagle._Interfaces.Private;
using Eagle._Interfaces.Public;

namespace Eagle._Components.Private
{
    [ObjectId("54a4fa59-05e7-4c54-ad72-d5496758c582")]
    internal sealed class EngineContext : IEngineContext, IDisposable
    {
        #region Public Constructors
        public EngineContext(int threadId)
        {
            this.threadId = threadId;

            levels = 0;
            previousLevels = 0;
            parserLevels = 0;
            expressionLevels = 0;
            traceLevels = 0;

#if DEBUGGER
            watchpointLevels = 0;
#endif

#if NOTIFY || NOTIFY_OBJECT
            notifyLevels = 0;
            notifyTypes = NotifyType.None;
            notifyFlags = NotifyFlags.None;
#endif

            policyLevels = 0;
            testLevels = 0;

            stackOverflow = false;

#if DEBUGGER
            debugger = null;
            interactiveLoopCallback = null;
#endif

#if SHELL
            argumentCallback = null;
            evaluateScriptCallback = null;
            evaluateFileCallback = null;
            evaluateEncodedFileCallback = null;
#endif

#if PREVIOUS_RESULT
            previousResult = null;
#endif

            engineFlags = EngineFlags.None;

            parseState = null;

            returnCode = ReturnCode.Ok;

            errorLine = 0;
            errorCode = null;
            errorInfo = null;
            errorFrames = 0;

            scriptLocation = null;
            scriptLocations = new StackList<IScriptLocation>();

            previousProcessId = 0;

            arraySearches = new ArraySearchDictionary();

#if HISTORY
            historyEngineFilter = null;
            history = new ClientDataList();
#endif

            complaint = null;
        }
        #endregion

        ///////////////////////////////////////////////////////////////////////

        #region IThreadContext Members
        public bool Disposed
        {
            get { return disposed; }
        }

        ///////////////////////////////////////////////////////////////////////

        private int threadId;
        public int ThreadId
        {
            get
            {
                //
                // NOTE: *EXEMPT* Hot path.
                //
                // CheckDisposed();

                return threadId;
            }
        }
        #endregion

        ///////////////////////////////////////////////////////////////////////

        #region IEngineContext Members
        private int levels;
        public int Levels
        {
            get { CheckDisposed(); return levels; }
            set { CheckDisposed(); levels = value; }
        }

        ///////////////////////////////////////////////////////////////////////

        private int previousLevels;
        public int PreviousLevels
        {
            get { CheckDisposed(); return previousLevels; }
            set { CheckDisposed(); previousLevels = value; }
        }

        ///////////////////////////////////////////////////////////////////////

        private int parserLevels;
        public int ParserLevels
        {
            get { CheckDisposed(); return parserLevels; }
            set { CheckDisposed(); parserLevels = value; }
        }

        ///////////////////////////////////////////////////////////////////////

        private int expressionLevels;
        public int ExpressionLevels
        {
            get { CheckDisposed(); return expressionLevels; }
            set { CheckDisposed(); expressionLevels = value; }
        }

        ///////////////////////////////////////////////////////////////////////

        private int unknownLevels;
        public int UnknownLevels
        {
            get { CheckDisposed(); return unknownLevels; }
            set { CheckDisposed(); unknownLevels = value; }
        }

        ///////////////////////////////////////////////////////////////////////

        private int traceLevels;
        public int TraceLevels
        {
            get { CheckDisposed(); return traceLevels; }
            set { CheckDisposed(); traceLevels = value; }
        }

        ///////////////////////////////////////////////////////////////////////

#if DEBUGGER
        private int watchpointLevels;
        public int WatchpointLevels
        {
            get { CheckDisposed(); return watchpointLevels; }
            set { CheckDisposed(); watchpointLevels = value; }
        }
#endif

        ///////////////////////////////////////////////////////////////////////

#if NOTIFY || NOTIFY_OBJECT
        private int notifyLevels;
        public int NotifyLevels
        {
            get { CheckDisposed(); return notifyLevels; }
            set { CheckDisposed(); notifyLevels = value; }
        }

        ///////////////////////////////////////////////////////////////////////

        private NotifyType notifyTypes;
        public NotifyType NotifyTypes
        {
            get { CheckDisposed(); return notifyTypes; }
            set { CheckDisposed(); notifyTypes = value; }
        }

        ///////////////////////////////////////////////////////////////////////

        private NotifyFlags notifyFlags;
        public NotifyFlags NotifyFlags
        {
            get { CheckDisposed(); return notifyFlags; }
            set { CheckDisposed(); notifyFlags = value; }
        }
#endif

        ///////////////////////////////////////////////////////////////////////

        private int policyLevels;
        public int PolicyLevels
        {
            get { CheckDisposed(); return policyLevels; }
            set { CheckDisposed(); policyLevels = value; }
        }

        ///////////////////////////////////////////////////////////////////////

        private int testLevels;
        public int TestLevels
        {
            get { CheckDisposed(); return testLevels; }
            set { CheckDisposed(); testLevels = value; }
        }

        ///////////////////////////////////////////////////////////////////////

        private bool stackOverflow;
        public bool StackOverflow
        {
            get { CheckDisposed(); return stackOverflow; }
            set { CheckDisposed(); stackOverflow = value; }
        }

        ///////////////////////////////////////////////////////////////////////

#if DEBUGGER
        private IDebugger debugger;
        public IDebugger Debugger
        {
            get { CheckDisposed(); return debugger; }
            set { CheckDisposed(); debugger = value; }
        }

        ///////////////////////////////////////////////////////////////////////

        private InteractiveLoopCallback interactiveLoopCallback;
        public InteractiveLoopCallback InteractiveLoopCallback
        {
            get { CheckDisposed(); return interactiveLoopCallback; }
            set { CheckDisposed(); interactiveLoopCallback = value; }
        }
#endif

        ///////////////////////////////////////////////////////////////////////

#if SHELL
        private ArgumentCallback argumentCallback;
        public ArgumentCallback ArgumentCallback
        {
            get { CheckDisposed(); return argumentCallback; }
            set { CheckDisposed(); argumentCallback = value; }
        }

        ///////////////////////////////////////////////////////////////////////

        private EvaluateScriptCallback evaluateScriptCallback;
        public EvaluateScriptCallback EvaluateScriptCallback
        {
            get { CheckDisposed(); return evaluateScriptCallback; }
            set { CheckDisposed(); evaluateScriptCallback = value; }
        }

        ///////////////////////////////////////////////////////////////////////

        private EvaluateFileCallback evaluateFileCallback;
        public EvaluateFileCallback EvaluateFileCallback
        {
            get { CheckDisposed(); return evaluateFileCallback; }
            set { CheckDisposed(); evaluateFileCallback = value; }
        }

        ///////////////////////////////////////////////////////////////////////

        private EvaluateEncodedFileCallback evaluateEncodedFileCallback;
        public EvaluateEncodedFileCallback EvaluateEncodedFileCallback
        {
            get { CheckDisposed(); return evaluateEncodedFileCallback; }
            set { CheckDisposed(); evaluateEncodedFileCallback = value; }
        }
#endif

        ///////////////////////////////////////////////////////////////////////

#if PREVIOUS_RESULT
        private Result previousResult;
        public Result PreviousResult
        {
            get { CheckDisposed(); return previousResult; }
            set { CheckDisposed(); previousResult = value; }
        }
#endif

        ///////////////////////////////////////////////////////////////////////

        private EngineFlags engineFlags;
        public EngineFlags EngineFlags
        {
            get { CheckDisposed(); return engineFlags; }
            set { CheckDisposed(); engineFlags = value; }
        }

        ///////////////////////////////////////////////////////////////////////

        private IParseState parseState;
        public IParseState ParseState
        {
            get { CheckDisposed(); return parseState; }
            set { CheckDisposed(); parseState = value; }
        }

        ///////////////////////////////////////////////////////////////////////

        private ReturnCode returnCode;
        public ReturnCode ReturnCode
        {
            get { CheckDisposed(); return returnCode; }
            set { CheckDisposed(); returnCode = value; }
        }

        ///////////////////////////////////////////////////////////////////////

        private int errorLine;
        public int ErrorLine
        {
            get { CheckDisposed(); return errorLine; }
            set { CheckDisposed(); errorLine = value; }
        }

        ///////////////////////////////////////////////////////////////////////

        private string errorCode;
        public string ErrorCode
        {
            get { CheckDisposed(); return errorCode; }
            set { CheckDisposed(); errorCode = value; }
        }

        ///////////////////////////////////////////////////////////////////////

        private string errorInfo;
        public string ErrorInfo
        {
            get { CheckDisposed(); return errorInfo; }
            set { CheckDisposed(); errorInfo = value; }
        }

        ///////////////////////////////////////////////////////////////////////

        private int errorFrames;
        public int ErrorFrames
        {
            get { CheckDisposed(); return errorFrames; }
            set { CheckDisposed(); errorFrames = value; }
        }

        ///////////////////////////////////////////////////////////////////////

        private IScriptLocation scriptLocation;
        public IScriptLocation ScriptLocation
        {
            get { CheckDisposed(); return scriptLocation; }
            set { CheckDisposed(); scriptLocation = value; }
        }

        ///////////////////////////////////////////////////////////////////////

        private StackList<IScriptLocation> scriptLocations;
        public StackList<IScriptLocation> ScriptLocations
        {
            get { CheckDisposed(); return scriptLocations; }
            set { CheckDisposed(); scriptLocations = value; }
        }

        ///////////////////////////////////////////////////////////////////////

        private int previousProcessId;
        public int PreviousProcessId
        {
            get { CheckDisposed(); return previousProcessId; }
            set { CheckDisposed(); previousProcessId = value; }
        }

        ///////////////////////////////////////////////////////////////////////

        private ArraySearchDictionary arraySearches;
        public ArraySearchDictionary ArraySearches
        {
            get { CheckDisposed(); return arraySearches; }
            set { CheckDisposed(); arraySearches = value; }
        }

        ///////////////////////////////////////////////////////////////////////

#if HISTORY
        private IHistoryFilter historyEngineFilter;
        public IHistoryFilter HistoryEngineFilter
        {
            get { CheckDisposed(); return historyEngineFilter; }
            set { CheckDisposed(); historyEngineFilter = value; }
        }

        ///////////////////////////////////////////////////////////////////////

        private ClientDataList history;
        public ClientDataList History
        {
            get { CheckDisposed(); return history; }
            set { CheckDisposed(); history = value; }
        }
#endif

        ///////////////////////////////////////////////////////////////////////

        private string complaint;
        public string Complaint
        {
            get { CheckDisposed(); return complaint; }
            set { CheckDisposed(); complaint = value; }
        }

        ///////////////////////////////////////////////////////////////////////

        public EngineFlags BeginExternalExecution()
        {
            CheckDisposed();

            levels++;

            engineFlags |= EngineFlags.ExternalExecution;
            return Engine.AddStackCheckFlags(ref engineFlags);
        }

        ///////////////////////////////////////////////////////////////////////

        public int EndExternalExecution(
            EngineFlags savedEngineFlags
            )
        {
            CheckDisposed();

            Engine.RemoveStackCheckFlags(savedEngineFlags, ref engineFlags);
            engineFlags &= ~EngineFlags.ExternalExecution;

            return --levels;
        }

        ///////////////////////////////////////////////////////////////////////

        public int BeginNestedExecution()
        {
            CheckDisposed();

            int savedPreviousLevels = previousLevels;
            previousLevels = levels;

            return savedPreviousLevels;
        }

        ///////////////////////////////////////////////////////////////////////

        public void EndNestedExecution(
            int savedPreviousLevels
            )
        {
            CheckDisposed();

            previousLevels = savedPreviousLevels;
        }
        #endregion

        ///////////////////////////////////////////////////////////////////////

        #region IDisposable "Pattern" Members
        private bool disposed;
        private void CheckDisposed() /* throw */
        {
#if THROW_ON_DISPOSED
            if (disposed && Engine.IsThrowOnDisposed(null, false))
                throw new InterpreterDisposedException(typeof(EngineContext));
#endif
        }

        ///////////////////////////////////////////////////////////////////////

        private /* protected virtual */ void Dispose(
            bool disposing
            )
        {
            TraceOps.DebugTrace(String.Format(
                "{0}: Dispose: called, disposing = {1}, disposed = {2}{3}",
                GlobalState.GetCurrentThreadId(), disposing, disposed,
                Environment.NewLine), typeof(EngineContext).Name);

            if (!disposed)
            {
                if (disposing)
                {
                    ////////////////////////////////////
                    // dispose managed resources here...
                    ////////////////////////////////////

                    threadId = 0;

                    ///////////////////////////////////////////////////////////

                    levels = 0;
                    previousLevels = 0;
                    parserLevels = 0;
                    expressionLevels = 0;
                    unknownLevels = 0;
                    traceLevels = 0;

#if DEBUGGER
                    watchpointLevels = 0;
#endif

#if NOTIFY || NOTIFY_OBJECT
                    notifyLevels = 0;
                    notifyTypes = NotifyType.None;
                    notifyFlags = NotifyFlags.None;
#endif

                    policyLevels = 0;
                    testLevels = 0;
                    stackOverflow = false;

#if DEBUGGER
                    interactiveLoopCallback = null;

                    if (debugger != null)
                    {
                        IDisposable disposable = debugger as IDisposable;

                        if (disposable != null)
                        {
                            disposable.Dispose();
                            disposable = null;
                        }

                        debugger = null;
                    }
#endif

#if SHELL
                    argumentCallback = null;
                    evaluateScriptCallback = null;
                    evaluateFileCallback = null;
                    evaluateEncodedFileCallback = null;
#endif

#if PREVIOUS_RESULT
                    previousResult = null;
#endif

                    engineFlags = EngineFlags.None;
                    parseState = null;
                    returnCode = ReturnCode.Ok;
                    errorLine = 0;
                    errorCode = null;
                    errorInfo = null;
                    errorFrames = 0;
                    scriptLocation = null;

                    ///////////////////////////////////////////////////////////

                    if (scriptLocations != null)
                    {
                        scriptLocations.Clear();
                        scriptLocations = null;
                    }

                    ///////////////////////////////////////////////////////////

                    previousProcessId = 0;

                    ///////////////////////////////////////////////////////////

                    if (arraySearches != null)
                    {
                        arraySearches.Clear();
                        arraySearches = null;
                    }

                    ///////////////////////////////////////////////////////////

#if HISTORY
                    historyEngineFilter = null;

                    if (history != null)
                    {
                        history.Clear();
                        history = null;
                    }
#endif

                    ///////////////////////////////////////////////////////////

                    complaint = null;
                }

                //////////////////////////////////////
                // release unmanaged resources here...
                //////////////////////////////////////

                disposed = true;
            }
        }
        #endregion

        ///////////////////////////////////////////////////////////////////////

        #region IDisposable Members
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
        #endregion

        ///////////////////////////////////////////////////////////////////////

        #region Destructor
        ~EngineContext()
        {
            Dispose(false);
        }
        #endregion
    }
}
