/*
 * ContextManager.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 System.Collections.Generic;
using System.Threading;
using Eagle._Attributes;
using Eagle._Components.Public;
using Eagle._Containers.Private;
using Eagle._Containers.Public;
using Eagle._Interfaces.Private;
using Eagle._Interfaces.Public;

namespace Eagle._Components.Private
{
    [ObjectId("53f5f2e0-ea9c-46a8-b584-779ef217beb5")]
    internal sealed class ContextManager :
            IContextManager, IHaveInterpreter, IDisposable
    {
        #region Private Static Data
        private static object syncRoot = new object();

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

        private static LocalDataStoreSlot engineSlot;
        private static LocalDataStoreSlot interactiveSlot;
        private static LocalDataStoreSlot testSlot;
        private static LocalDataStoreSlot variableSlot;
        #endregion

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

        #region Private Data
        private IEngineContext previousEngineContext;
        private IInteractiveContext previousInteractiveContext;
        private ITestContext previousTestContext;
        private IVariableContext previousVariableContext;
        #endregion

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

        #region Public Static Methods
        public static void Initialize()
        {
            //
            // NOTE: These MUST to be done prior to evaluating any scripts or
            //       call frame handling (and a bunch of other stuff) will not
            //       work properly.
            //
            lock (syncRoot)
            {
                if (engineSlot == null)
                    engineSlot = Thread.AllocateDataSlot();

                if (interactiveSlot == null)
                    interactiveSlot = Thread.AllocateDataSlot();

                if (testSlot == null)
                    testSlot = Thread.AllocateDataSlot();

                if (variableSlot == null)
                    variableSlot = Thread.AllocateDataSlot();
            }
        }

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

        //
        // NOTE: This method is called via the interpreter disposal pipeline.
        //
        public static int Purge(
            bool nonPrimary
            )
        {
            return PrivatePurgeEngineContexts(nonPrimary) +
                PrivatePurgeInteractiveContexts(nonPrimary) +
                PrivatePurgeTestContexts(nonPrimary) +
                PrivatePurgeVariableContexts(nonPrimary);
        }
        #endregion

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

        #region Public Constructors
        public ContextManager(
            Interpreter interpreter
            )
        {
            this.interpreter = interpreter;
        }
        #endregion

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

        #region Private Static Methods
        private static int GetCurrentThreadId()
        {
            return GlobalState.GetCurrentThreadId();
        }

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

        private static bool CanUseThreadContext(
            IThreadContext threadContext
            )
        {
            if ((threadContext == null) || threadContext.Disposed)
                return false;

            return (threadContext.ThreadId == GetCurrentThreadId());
        }

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

        private static IEnumerable<IInterpreter> GetInterpretersToPurge(
            IEnumerable<IInterpreter> interpreters,
            bool nonPrimary
            )
        {
            IEnumerable<IInterpreter> newInterpreters;

            newInterpreters = GlobalState.FilterInterpreters(
                interpreters, false, nonPrimary);

            //
            // HACK: If an interpreter is present on the "active stack",
            //       we never want to purge its contexts.
            //
            newInterpreters = GlobalState.FilterActiveInterpreters(
                newInterpreters, false, false);

            return newInterpreters;
        }

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

        private static int PrivatePurgeEngineContexts(
            bool nonPrimary
            )
        {
            try
            {
                LocalDataStoreSlot localSlot;

                lock (syncRoot)
                {
                    localSlot = engineSlot;
                }

                if (localSlot == null)
                    return 0;

                InterpreterEngineContextDictionary contexts =
                    Thread.GetData(localSlot)
                    as InterpreterEngineContextDictionary; /* throw */

                if (contexts == null)
                    return 0;

                IEnumerable<IInterpreter> interpreters =
                    GetInterpretersToPurge(contexts.Keys, nonPrimary);

                if (interpreters == null)
                    return 0;

                int count = 0;

                foreach (IInterpreter interpreter in interpreters)
                {
                    //
                    // NOTE: Does the requested interpreter have an
                    //       entry in the dictionary?  It should,
                    //       that is where we got it from originally.
                    //
                    if (contexts.Remove(interpreter))
                        count++;

                    //
                    // NOTE: If there are no more contexts present,
                    //       free the collection and the thread data
                    //       slot.
                    //
                    if (contexts.Count == 0)
                    {
                        //
                        // NOTE: Release local reference now.
                        //
                        contexts = null;

                        //
                        // NOTE: Clear it in the per-thread data.
                        //
                        Thread.SetData(localSlot, contexts);

                        //
                        // NOTE: There is nothing more to do.
                        //
                        break;
                    }
                }

                //
                // NOTE: Even if the loop above was skipped completely,
                //       clear out the per-thread data if necessary.
                //
                if ((contexts != null) && (contexts.Count == 0))
                {
                    //
                    // NOTE: Release local reference now.
                    //
                    contexts = null;

                    //
                    // NOTE: Clear it in the per-thread data.
                    //
                    Thread.SetData(localSlot, contexts);
                }

                TraceOps.DebugTrace(String.Format(
                    "{0}: PrivatePurgeEngineContexts: nonPrimary = {1}, " +
                    "count = {2}{3}", GlobalState.GetCurrentThreadId(),
                    nonPrimary, count, Environment.NewLine),
                    typeof(ContextManager).Name);

                return count;
            }
            catch
            {
                // do nothing.
            }

            return 0;
        }

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

        private static int PrivatePurgeInteractiveContexts(
            bool nonPrimary
            )
        {
            try
            {
                LocalDataStoreSlot localSlot;

                lock (syncRoot)
                {
                    localSlot = interactiveSlot;
                }

                if (localSlot == null)
                    return 0;

                InterpreterInteractiveContextDictionary contexts =
                    Thread.GetData(localSlot) as
                    InterpreterInteractiveContextDictionary; /* throw */

                if (contexts == null)
                    return 0;

                IEnumerable<IInterpreter> interpreters =
                    GetInterpretersToPurge(contexts.Keys, nonPrimary);

                if (interpreters == null)
                    return 0;

                int count = 0;

                foreach (IInterpreter interpreter in interpreters)
                {
                    //
                    // NOTE: Does the requested interpreter have an
                    //       entry in the dictionary?  It should,
                    //       that is where we got it from originally.
                    //
                    if (contexts.Remove(interpreter))
                        count++;

                    //
                    // NOTE: If there are no more contexts present,
                    //       free the collection and the thread data
                    //       slot.
                    //
                    if (contexts.Count == 0)
                    {
                        //
                        // NOTE: Release local reference now.
                        //
                        contexts = null;

                        //
                        // NOTE: Clear it in the per-thread data.
                        //
                        Thread.SetData(localSlot, contexts);

                        //
                        // NOTE: There is nothing more to do.
                        //
                        break;
                    }
                }

                //
                // NOTE: Even if the loop above was skipped completely,
                //       clear out the per-thread data if necessary.
                //
                if ((contexts != null) && (contexts.Count == 0))
                {
                    //
                    // NOTE: Release local reference now.
                    //
                    contexts = null;

                    //
                    // NOTE: Clear it in the per-thread data.
                    //
                    Thread.SetData(localSlot, contexts);
                }

                TraceOps.DebugTrace(String.Format(
                    "{0}: PrivatePurgeInteractiveContexts: nonPrimary = {1}, " +
                    "count = {2}{3}", GlobalState.GetCurrentThreadId(),
                    nonPrimary, count, Environment.NewLine),
                    typeof(ContextManager).Name);

                return count;
            }
            catch
            {
                // do nothing.
            }

            return 0;
        }

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

        private static int PrivatePurgeTestContexts(
            bool nonPrimary
            )
        {
            try
            {
                LocalDataStoreSlot localSlot;

                lock (syncRoot)
                {
                    localSlot = testSlot;
                }

                if (localSlot == null)
                    return 0;

                InterpreterTestContextDictionary contexts =
                    Thread.GetData(localSlot) as
                    InterpreterTestContextDictionary; /* throw */

                if (contexts == null)
                    return 0;

                IEnumerable<IInterpreter> interpreters =
                    GetInterpretersToPurge(contexts.Keys, nonPrimary);

                if (interpreters == null)
                    return 0;

                int count = 0;

                foreach (IInterpreter interpreter in interpreters)
                {
                    //
                    // NOTE: Does the requested interpreter have an
                    //       entry in the dictionary?  It should,
                    //       that is where we got it from originally.
                    //
                    if (contexts.Remove(interpreter))
                        count++;

                    //
                    // NOTE: If there are no more contexts present,
                    //       free the collection and the thread data
                    //       slot.
                    //
                    if (contexts.Count == 0)
                    {
                        //
                        // NOTE: Release local reference now.
                        //
                        contexts = null;

                        //
                        // NOTE: Clear it in the per-thread data.
                        //
                        Thread.SetData(localSlot, contexts);

                        //
                        // NOTE: There is nothing more to do.
                        //
                        break;
                    }
                }

                //
                // NOTE: Even if the loop above was skipped completely,
                //       clear out the per-thread data if necessary.
                //
                if ((contexts != null) && (contexts.Count == 0))
                {
                    //
                    // NOTE: Release local reference now.
                    //
                    contexts = null;

                    //
                    // NOTE: Clear it in the per-thread data.
                    //
                    Thread.SetData(localSlot, contexts);
                }

                TraceOps.DebugTrace(String.Format(
                    "{0}: PrivatePurgeTestContexts: nonPrimary = {1}, " +
                    "count = {2}{3}", GlobalState.GetCurrentThreadId(),
                    nonPrimary, count, Environment.NewLine),
                    typeof(ContextManager).Name);

                return count;
            }
            catch
            {
                // do nothing.
            }

            return 0;
        }

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

        private static int PrivatePurgeVariableContexts(
            bool nonPrimary
            )
        {
            try
            {
                LocalDataStoreSlot localSlot;

                lock (syncRoot)
                {
                    localSlot = variableSlot;
                }

                if (localSlot == null)
                    return 0;

                InterpreterVariableContextDictionary contexts =
                    Thread.GetData(localSlot) as
                    InterpreterVariableContextDictionary; /* throw */

                if (contexts == null)
                    return 0;

                IEnumerable<IInterpreter> interpreters =
                    GetInterpretersToPurge(contexts.Keys, nonPrimary);

                if (interpreters == null)
                    return 0;

                int count = 0;

                foreach (IInterpreter interpreter in interpreters)
                {
                    //
                    // NOTE: Does the requested interpreter have an
                    //       entry in the dictionary?  It should,
                    //       that is where we got it from originally.
                    //
                    if (contexts.Remove(interpreter))
                        count++;

                    //
                    // NOTE: If there are no more contexts present,
                    //       free the collection and the thread data
                    //       slot.
                    //
                    if (contexts.Count == 0)
                    {
                        //
                        // NOTE: Release local reference now.
                        //
                        contexts = null;

                        //
                        // NOTE: Clear it in the per-thread data.
                        //
                        Thread.SetData(localSlot, contexts);

                        //
                        // NOTE: There is nothing more to do.
                        //
                        break;
                    }
                }

                //
                // NOTE: Even if the loop above was skipped completely,
                //       clear out the per-thread data if necessary.
                //
                if ((contexts != null) && (contexts.Count == 0))
                {
                    //
                    // NOTE: Release local reference now.
                    //
                    contexts = null;

                    //
                    // NOTE: Clear it in the per-thread data.
                    //
                    Thread.SetData(localSlot, contexts);
                }

                TraceOps.DebugTrace(String.Format(
                    "{0}: PrivatePurgeVariableContexts: nonPrimary = {1}, " +
                    "count = {2}{3}", GlobalState.GetCurrentThreadId(),
                    nonPrimary, count, Environment.NewLine),
                    typeof(ContextManager).Name);

                return count;
            }
            catch
            {
                // do nothing.
            }

            return 0;
        }
        #endregion

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

        #region Private Methods
        private bool PrivateFreeEngineContext()
        {
            Result error = null;

            return PrivateFreeEngineContext(ref error);
        }

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

        private bool PrivateFreeEngineContext(
            ref Result error
            )
        {
            try
            {
                if (interpreter == null)
                {
                    error = "invalid interpreter";
                    return false;
                }

                LocalDataStoreSlot localSlot;

                lock (syncRoot)
                {
                    localSlot = engineSlot;
                }

                if (localSlot == null)
                {
                    error = "engine context slot is invalid";
                    return false;
                }

                //
                // NOTE: Try to obtain the per-interpreter dictionary
                //       of contexts for this thread.
                //
                InterpreterEngineContextDictionary contexts =
                    Thread.GetData(localSlot) as
                    InterpreterEngineContextDictionary; /* throw */

                if (contexts != null)
                {
                    //
                    // NOTE: Does the requested interpreter have an
                    //       entry in the dictionary?
                    //
                    /* IGNORED */
                    contexts.Remove(interpreter);

                    //
                    // NOTE: If there are no more contexts present,
                    //       free the collection and the thread data
                    //       slot.
                    //
                    if (contexts.Count == 0)
                    {
                        //
                        // NOTE: Release local reference now.
                        //
                        contexts = null;

                        //
                        // NOTE: Clear it in the per-thread data.
                        //
                        Thread.SetData(localSlot, contexts);
                    }
                }

                //
                // NOTE: Invalidate the cached context for this
                //       interpreter.
                //
                previousEngineContext = null;

                return true;
            }
            catch (Exception e)
            {
                error = e;
            }

            return false;
        }

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

        private IEngineContext PrivateGetEngineContext(
            bool create
            )
        {
            Result error = null;

            return PrivateGetEngineContext(create, ref error);
        }

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

        private IEngineContext PrivateGetEngineContext(
            bool create,
            ref Result error
            )
        {
            try
            {
                if (interpreter == null)
                {
                    error = "invalid interpreter";
                    return null;
                }

                IEngineContext result = previousEngineContext;

                if (CanUseThreadContext(result))
                    return result;

                LocalDataStoreSlot localSlot;

                lock (syncRoot)
                {
                    localSlot = engineSlot;
                }

                if (localSlot == null)
                {
                    error = "engine context slot is invalid";
                    return null;
                }

                //
                // NOTE: Try to obtain the per-interpreter dictionary
                //       of engine contexts for this thread.
                //
                InterpreterEngineContextDictionary contexts =
                    Thread.GetData(localSlot) as
                    InterpreterEngineContextDictionary; /* throw */

                //
                // NOTE: Has the dictionary been created yet and/or can
                //       we create it now?
                //
                if (create && (contexts == null))
                {
                    //
                    // NOTE: Nope, create it now.
                    //
                    contexts = new InterpreterEngineContextDictionary();

                    //
                    // NOTE: Store it in the per-thread data.
                    //
                    Thread.SetData(localSlot, contexts); /* throw */
                }

                //
                // NOTE: Is the dictionary available now (if not,
                //       we have been forbidden by the caller from
                //       automatically creating it).
                //
                if (contexts != null)
                {
                    //
                    // NOTE: Does the requested interpreter have an
                    //       entry in the dictionary?  If so, grab
                    //       and return it.
                    //
                    if (!contexts.TryGetValue(
                            interpreter, out result))
                    {
                        //
                        // NOTE: Now, create one and add it to the
                        //       dictionary of engine contexts.
                        //
                        result = new EngineContext(
                            GetCurrentThreadId());

                        contexts.Add(interpreter, result);
                    }

                    //
                    // NOTE: Save the resulting context for next
                    //       time.
                    //
                    previousEngineContext = result;

                    return result;
                }
                else
                {
                    error = "engine contexts not available";
                }
            }
            catch (Exception e)
            {
                error = e;
            }

            return null;
        }

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

        private int PrivateGetEngineContextCount()
        {
            try
            {
                LocalDataStoreSlot localSlot;

                lock (syncRoot)
                {
                    localSlot = engineSlot;
                }

                if (localSlot == null)
                    return 0;

                InterpreterEngineContextDictionary contexts =
                    Thread.GetData(localSlot) as
                    InterpreterEngineContextDictionary; /* throw */

                if (contexts != null)
                    return contexts.Count;
            }
            catch
            {
                // do nothing.
            }

            return 0;
        }

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

        private bool PrivateFreeInteractiveContext()
        {
            Result error = null;

            return PrivateFreeInteractiveContext(ref error);
        }

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

        private bool PrivateFreeInteractiveContext(
            ref Result error
            )
        {
            try
            {
                if (interpreter == null)
                {
                    error = "invalid interpreter";
                    return false;
                }

                LocalDataStoreSlot localSlot;

                lock (syncRoot)
                {
                    localSlot = interactiveSlot;
                }

                if (localSlot == null)
                {
                    error = "interactive context slot is invalid";
                    return false;
                }

                //
                // NOTE: Try to obtain the per-interpreter dictionary
                //       of contexts for this thread.
                //
                InterpreterInteractiveContextDictionary contexts =
                    Thread.GetData(localSlot) as
                    InterpreterInteractiveContextDictionary; /* throw */

                if (contexts != null)
                {
                    //
                    // NOTE: Does the requested interpreter have an
                    //       entry in the dictionary?
                    //
                    /* IGNORED */
                    contexts.Remove(interpreter);

                    //
                    // NOTE: If there are no more contexts present,
                    //       free the collection and the thread data
                    //       slot.
                    //
                    if (contexts.Count == 0)
                    {
                        //
                        // NOTE: Release local reference now.
                        //
                        contexts = null;

                        //
                        // NOTE: Clear it in the per-thread data.
                        //
                        Thread.SetData(localSlot, contexts);
                    }
                }

                //
                // NOTE: Invalidate the cached context for this
                //       interpreter.
                //
                previousInteractiveContext = null;

                return true;
            }
            catch (Exception e)
            {
                error = e;
            }

            return false;
        }

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

        private IInteractiveContext PrivateGetInteractiveContext(
            bool create
            )
        {
            Result error = null;

            return PrivateGetInteractiveContext(create, ref error);
        }

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

        private IInteractiveContext PrivateGetInteractiveContext(
            bool create,
            ref Result error
            )
        {
            try
            {
                if (interpreter == null)
                {
                    error = "invalid interpreter";
                    return null;
                }

                IInteractiveContext result = previousInteractiveContext;

                if (CanUseThreadContext(result))
                    return result;

                LocalDataStoreSlot localSlot;

                lock (syncRoot)
                {
                    localSlot = interactiveSlot;
                }

                if (localSlot == null)
                {
                    error = "interactive context slot is invalid";
                    return null;
                }

                //
                // NOTE: Try to obtain the per-interpreter dictionary
                //       of interactive contexts for this thread.
                //
                InterpreterInteractiveContextDictionary contexts =
                    Thread.GetData(localSlot) as
                    InterpreterInteractiveContextDictionary; /* throw */

                //
                // NOTE: Has the dictionary been created yet and/or can
                //       we create it now?
                //
                if (create && (contexts == null))
                {
                    //
                    // NOTE: Nope, create it now.
                    //
                    contexts = new InterpreterInteractiveContextDictionary();

                    //
                    // NOTE: Store it in the per-thread data.
                    //
                    Thread.SetData(localSlot, contexts); /* throw */
                }

                //
                // NOTE: Is the dictionary available now (if not,
                //       we have been forbidden by the caller from
                //       automatically creating it).
                //
                if (contexts != null)
                {
                    //
                    // NOTE: Does the requested interpreter have an
                    //       entry in the dictionary?  If so, grab
                    //       and return it.
                    //
                    if (!contexts.TryGetValue(
                            interpreter, out result))
                    {
                        //
                        // NOTE: Now, create one and add it to the
                        //       dictionary of interactive contexts.
                        //
                        result = new InteractiveContext(
                            GetCurrentThreadId());

                        contexts.Add(interpreter, result);
                    }

                    //
                    // NOTE: Save the resulting context for next
                    //       time.
                    //
                    previousInteractiveContext = result;

                    return result;
                }
                else
                {
                    error = "interactive contexts not available";
                }
            }
            catch (Exception e)
            {
                error = e;
            }

            return null;
        }

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

        private int PrivateGetInteractiveContextCount()
        {
            try
            {
                LocalDataStoreSlot localSlot;

                lock (syncRoot)
                {
                    localSlot = interactiveSlot;
                }

                if (localSlot == null)
                    return 0;

                InterpreterInteractiveContextDictionary contexts =
                    Thread.GetData(localSlot) as
                    InterpreterInteractiveContextDictionary; /* throw */

                if (contexts != null)
                    return contexts.Count;
            }
            catch
            {
                // do nothing.
            }

            return 0;
        }

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

        private bool PrivateFreeTestContext()
        {
            Result error = null;

            return PrivateFreeTestContext(ref error);
        }

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

        private bool PrivateFreeTestContext(
            ref Result error
            )
        {
            try
            {
                if (interpreter == null)
                {
                    error = "invalid interpreter";
                    return false;
                }

                LocalDataStoreSlot localSlot;

                lock (syncRoot)
                {
                    localSlot = testSlot;
                }

                if (localSlot == null)
                {
                    error = "test context slot is invalid";
                    return false;
                }

                //
                // NOTE: Try to obtain the per-interpreter dictionary
                //       of contexts for this thread.
                //
                InterpreterTestContextDictionary contexts =
                    Thread.GetData(localSlot) as
                    InterpreterTestContextDictionary; /* throw */

                if (contexts != null)
                {
                    //
                    // NOTE: Does the requested interpreter have an
                    //       entry in the dictionary?
                    //
                    /* IGNORED */
                    contexts.Remove(interpreter);

                    //
                    // NOTE: If there are no more contexts present,
                    //       free the collection and the thread data
                    //       slot.
                    //
                    if (contexts.Count == 0)
                    {
                        //
                        // NOTE: Release local reference now.
                        //
                        contexts = null;

                        //
                        // NOTE: Clear it in the per-thread data.
                        //
                        Thread.SetData(localSlot, contexts);
                    }
                }

                //
                // NOTE: Invalidate the cached context for this
                //       interpreter.
                //
                previousTestContext = null;

                return true;
            }
            catch (Exception e)
            {
                error = e;
            }

            return false;
        }

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

        private ITestContext PrivateGetTestContext(
            bool create
            )
        {
            Result error = null;

            return PrivateGetTestContext(create, ref error);
        }

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

        private ITestContext PrivateGetTestContext(
            bool create,
            ref Result error
            )
        {
            try
            {
                if (interpreter == null)
                {
                    error = "invalid interpreter";
                    return null;
                }

                ITestContext result = previousTestContext;

                if (CanUseThreadContext(result))
                    return result;

                LocalDataStoreSlot localSlot;

                lock (syncRoot)
                {
                    localSlot = testSlot;
                }

                if (localSlot == null)
                {
                    error = "test context slot is invalid";
                    return null;
                }

                //
                // NOTE: Try to obtain the per-interpreter dictionary of
                //       test contexts for this thread.
                //
                InterpreterTestContextDictionary contexts =
                    Thread.GetData(localSlot) as
                    InterpreterTestContextDictionary; /* throw */

                //
                // NOTE: Has the dictionary been created yet and/or can
                //       we create it now?
                //
                if (create && (contexts == null))
                {
                    //
                    // NOTE: Nope, create it now.
                    //
                    contexts = new InterpreterTestContextDictionary();

                    //
                    // NOTE: Store it in the per-thread data.
                    //
                    Thread.SetData(localSlot, contexts); /* throw */
                }

                //
                // NOTE: Is the dictionary available now (if not,
                //       we have been forbidden by the caller from
                //       automatically creating it).
                //
                if (contexts != null)
                {
                    //
                    // NOTE: Does the requested interpreter have an
                    //       entry in the dictionary?  If so, grab
                    //       and return it.
                    //
                    if (!contexts.TryGetValue(
                            interpreter, out result))
                    {
                        //
                        // NOTE: Now, create one and add it to the
                        //       dictionary of test contexts.
                        //
                        result = new TestContext(
                            GetCurrentThreadId());

                        contexts.Add(interpreter, result);
                    }

                    //
                    // NOTE: Save the resulting context for next
                    //       time.
                    //
                    previousTestContext = result;

                    return result;
                }
                else
                {
                    error = "test contexts not available";
                }
            }
            catch (Exception e)
            {
                error = e;
            }

            return null;
        }

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

        private int PrivateGetTestContextCount()
        {
            try
            {
                LocalDataStoreSlot localSlot;

                lock (syncRoot)
                {
                    localSlot = testSlot;
                }

                if (localSlot == null)
                    return 0;

                InterpreterTestContextDictionary contexts =
                    Thread.GetData(localSlot) as
                    InterpreterTestContextDictionary; /* throw */

                if (contexts != null)
                    return contexts.Count;
            }
            catch
            {
                // do nothing.
            }

            return 0;
        }

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

        private bool PrivateFreeVariableContext()
        {
            Result error = null;

            return PrivateFreeVariableContext(ref error);
        }

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

        private bool PrivateFreeVariableContext(
            ref Result error
            )
        {
            try
            {
                if (interpreter == null)
                {
                    error = "invalid interpreter";
                    return false;
                }

                LocalDataStoreSlot localSlot;

                lock (syncRoot)
                {
                    localSlot = variableSlot;
                }

                if (localSlot == null)
                {
                    error = "variable context slot is invalid";
                    return false;
                }

                //
                // NOTE: Try to obtain the per-interpreter dictionary
                //       of contexts for this thread.
                //
                InterpreterVariableContextDictionary contexts =
                    Thread.GetData(localSlot) as
                    InterpreterVariableContextDictionary; /* throw */

                if (contexts != null)
                {
                    //
                    // NOTE: Does the requested interpreter have an
                    //       entry in the dictionary?
                    //
                    /* IGNORED */
                    contexts.Remove(interpreter);

                    //
                    // NOTE: If there are no more contexts present,
                    //       free the collection and the thread data
                    //       slot.
                    //
                    if (contexts.Count == 0)
                    {
                        //
                        // NOTE: Release local reference now.
                        //
                        contexts = null;

                        //
                        // NOTE: Clear it in the per-thread data.
                        //
                        Thread.SetData(localSlot, contexts);
                    }
                }

                //
                // NOTE: Invalidate the cached context for this
                //       interpreter.
                //
                previousVariableContext = null;

                return true;
            }
            catch (Exception e)
            {
                error = e;
            }

            return false;
        }

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

        private IVariableContext PrivateGetVariableContext(
            bool create
            )
        {
            Result error = null;

            return PrivateGetVariableContext(create, ref error);
        }

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

        private IVariableContext PrivateGetVariableContext(
            bool create,
            ref Result error
            )
        {
            try
            {
                if (interpreter == null)
                {
                    error = "invalid interpreter";
                    return null;
                }

                IVariableContext result = previousVariableContext;

                if (CanUseThreadContext(result))
                    return result;

                LocalDataStoreSlot localSlot;

                lock (syncRoot)
                {
                    localSlot = variableSlot;
                }

                if (localSlot == null)
                {
                    error = "variable context slot is invalid";
                    return null;
                }

                //
                // NOTE: Try to obtain the per-interpreter dictionary
                //       of variable contexts for this thread.
                //
                InterpreterVariableContextDictionary contexts =
                    Thread.GetData(localSlot) as
                    InterpreterVariableContextDictionary; /* throw */

                //
                // NOTE: Has the dictionary been created yet and/or can
                //       we create it now?
                //
                if (create && (contexts == null))
                {
                    //
                    // NOTE: Nope, create it now.
                    //
                    contexts = new InterpreterVariableContextDictionary();

                    //
                    // NOTE: Store it in the per-thread data.
                    //
                    Thread.SetData(localSlot, contexts); /* throw */
                }

                //
                // NOTE: Is the dictionary available now (if not,
                //       we have been forbidden by the caller from
                //       automatically creating it).
                //
                if (contexts != null)
                {
                    //
                    // NOTE: Does the requested interpreter have an
                    //       entry in the dictionary?  If so, grab
                    //       and return it.
                    //
                    if (!contexts.TryGetValue(
                            interpreter, out result))
                    {
                        //
                        // NOTE: Create a new call stack for the
                        //       interpreter.
                        //
                        CallStack callStack = new CallStack(
                            interpreter.RecursionLimit);

                        //
                        // NOTE: If necessary, create a new global
                        //       call frame for the interpreter;
                        //       all threads share the same global
                        //       call frame for a given interpreter.
                        //
                        lock (interpreter.SyncRoot) /* TRANSACTIONAL */
                        {
                            //
                            // NOTE: Does the interpreter already
                            //       have a global call frame?  If
                            //       not, make sure a new one is
                            //       created now.
                            //
                            /* IGNORED */
                            interpreter.CreateGlobalFrame(callStack);

                            //
                            // NOTE: Now, create one and add it to
                            //       the dictionary of variable
                            //       contexts.
                            //
                            result = new VariableContext(
                                GetCurrentThreadId(), callStack,
                                interpreter.InternalGlobalFrame,
                                null, null, null, null);

                            contexts.Add(interpreter, result);
                        }

                        //
                        // NOTE: This call frame is never popped.
                        //
                        interpreter.PushGlobalCallFrame(false);
                    }

                    //
                    // NOTE: Save the resulting context for next
                    //       time.
                    //
                    previousVariableContext = result;

                    return result;
                }
                else
                {
                    error = "variable contexts not available";
                }
            }
            catch (Exception e)
            {
                error = e;
            }

            return null;
        }

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

        private int PrivateGetVariableContextCount()
        {
            try
            {
                LocalDataStoreSlot localSlot;

                lock (syncRoot)
                {
                    localSlot = variableSlot;
                }

                if (localSlot == null)
                    return 0;

                InterpreterVariableContextDictionary contexts =
                    Thread.GetData(localSlot) as
                    InterpreterVariableContextDictionary; /* throw */

                if (contexts != null)
                    return contexts.Count;
            }
            catch
            {
                // do nothing.
            }

            return 0;
        }
        #endregion

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

        #region IGetInterpreter / ISetInterpreter Members
        private Interpreter interpreter;
        public Interpreter Interpreter
        {
            get { CheckDisposed(); return interpreter; }
            set { CheckDisposed(); interpreter = value; }
        }
        #endregion

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

        #region IContextManager Members
        //
        // NOTE: This method is called via the interpreter disposal pipeline.
        //
        public int GetInterpreterContextCount()
        {
            // CheckDisposed(); /* EXEMPT */

            return Math.Max(
                Math.Max(PrivateGetEngineContextCount(),
                PrivateGetInteractiveContextCount()),
                Math.Max(PrivateGetTestContextCount(),
                PrivateGetVariableContextCount()));
        }

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

        public bool FreeEngineContext()
        {
            CheckDisposed();

            return PrivateFreeEngineContext();
        }

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

        public bool FreeEngineContext(
            ref Result error
            )
        {
            CheckDisposed();

            return PrivateFreeEngineContext(ref error);
        }

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

        public IEngineContext GetEngineContext(
            bool create
            )
        {
            CheckDisposed();

            return PrivateGetEngineContext(create);
        }

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

        public IEngineContext GetEngineContext(
            bool create,
            ref Result error
            )
        {
            CheckDisposed();

            return PrivateGetEngineContext(create, ref error);
        }

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

        public int GetEngineContextCount()
        {
            CheckDisposed();

            return PrivateGetEngineContextCount();
        }

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

        public int PurgeEngineContexts(
            bool nonPrimary
            )
        {
            CheckDisposed();

            return PrivatePurgeEngineContexts(nonPrimary);
        }

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

        public bool FreeInteractiveContext()
        {
            CheckDisposed();

            return PrivateFreeInteractiveContext();
        }

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

        public bool FreeInteractiveContext(
            ref Result error
            )
        {
            CheckDisposed();

            return PrivateFreeInteractiveContext(ref error);
        }

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

        public IInteractiveContext GetInteractiveContext(
            bool create
            )
        {
            CheckDisposed();

            return PrivateGetInteractiveContext(create);
        }

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

        public IInteractiveContext GetInteractiveContext(
            bool create,
            ref Result error
            )
        {
            CheckDisposed();

            return PrivateGetInteractiveContext(create, ref error);
        }

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

        public int GetInteractiveContextCount()
        {
            CheckDisposed();

            return PrivateGetInteractiveContextCount();
        }

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

        public int PurgeInteractiveContexts(
            bool nonPrimary
            )
        {
            CheckDisposed();

            return PrivatePurgeInteractiveContexts(nonPrimary);
        }

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

        public bool FreeTestContext()
        {
            CheckDisposed();

            return PrivateFreeTestContext();
        }

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

        public bool FreeTestContext(
            ref Result error
            )
        {
            CheckDisposed();

            return PrivateFreeTestContext(ref error);
        }

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

        public ITestContext GetTestContext(
            bool create
            )
        {
            CheckDisposed();

            return PrivateGetTestContext(create);
        }

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

        public ITestContext GetTestContext(
            bool create,
            ref Result error
            )
        {
            CheckDisposed();

            return PrivateGetTestContext(create, ref error);
        }

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

        public int GetTestContextCount()
        {
            CheckDisposed();

            return PrivateGetTestContextCount();
        }

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

        public int PurgeTestContexts(
            bool nonPrimary
            )
        {
            CheckDisposed();

            return PrivatePurgeTestContexts(nonPrimary);
        }

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

        public bool FreeVariableContext()
        {
            CheckDisposed();

            return PrivateFreeVariableContext();
        }

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

        public bool FreeVariableContext(
            ref Result error
            )
        {
            CheckDisposed();

            return PrivateFreeVariableContext(ref error);
        }

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

        public IVariableContext GetVariableContext(
            bool create
            )
        {
            CheckDisposed();

            return PrivateGetVariableContext(create);
        }

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

        public IVariableContext GetVariableContext(
            bool create,
            ref Result error
            )
        {
            CheckDisposed();

            return PrivateGetVariableContext(create, ref error);
        }

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

        public int GetVariableContextCount()
        {
            CheckDisposed();

            return PrivateGetVariableContextCount();
        }

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

        public int PurgeVariableContexts(
            bool nonPrimary
            )
        {
            CheckDisposed();

            return PrivatePurgeVariableContexts(nonPrimary);
        }

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

        //
        // NOTE: This method is called via the interpreter disposal pipeline
        //       as well as during the disposal of this class.
        //
        public void Free(
            bool global
            )
        {
            // CheckDisposed(); /* EXEMPT */

            IDisposable disposable;

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

            disposable = PrivateGetEngineContext(false) as IDisposable;

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

            PrivateFreeEngineContext();

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

            disposable = PrivateGetInteractiveContext(false) as IDisposable;

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

            PrivateFreeInteractiveContext();

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

            disposable = PrivateGetTestContext(false) as IDisposable;

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

            PrivateFreeTestContext();

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

            //
            // HACK: *SPECIAL CASE* We cannot dispose the global call frame
            //       unless we are [also] disposing of the interpreter itself;
            //       therefore, use the special Free method here instead of the
            //       Dispose method.  The Free method is guaranteed to do the
            //       right thing with regard to the global call frame (assuming
            //       the "global" paramter is correct).
            //
            IVariableContext variableContext = PrivateGetVariableContext(false);

            if (variableContext != null)
            {
                variableContext.Free(global);
                variableContext = null;
            }

            PrivateFreeVariableContext();

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

            if (global && (interpreter != null))
                interpreter = null; /* NOT OWNED, DO NOT DISPOSE. */
        }
        #endregion

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

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

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

        private /* protected virtual */ void Dispose(
            bool disposing
            )
        {
            if (!disposed)
            {
                if (disposing)
                {
                    ////////////////////////////////////
                    // dispose managed resources here...
                    ////////////////////////////////////

                    //
                    // NOTE: Skip freeing any thread-local storage used by
                    //       this object if the entire application domain
                    //       is being finalized.  This is necessary because
                    //       the thread-local storage may have already been
                    //       freed in that case.
                    //
                    if (!AppDomainOps.IsFinalizing())
                        Free(true);
                }

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

                disposed = true;
            }
        }
        #endregion

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

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

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

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