/*
 * GlobalState.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.Globalization;
using System.IO;
using System.Reflection;

#if CAS_POLICY
using System.Security.Policy;
#endif

using System.Threading;
using Eagle._Attributes;
using Eagle._Components.Public;
using Eagle._Constants;
using Eagle._Containers.Private;
using Eagle._Containers.Public;
using Eagle._Interfaces.Public;

namespace Eagle._Components.Private
{
    [ObjectId("e8491fec-2fd3-455e-92fd-cf2a84c75e8a")]
    internal static class GlobalState
    {
        #region Private Read-Only Data (Logical Constants)
        private static readonly object syncRoot = new object();

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

        #region Default Package Name & Version Data
        private static readonly string DefaultPackageName = "Eagle";

        //
        // TODO: This version information should be changed if the major or
        //       minor version of the assembly changes.
        //
        private static readonly int DefaultMajorVersion = 1;
        private static readonly int DefaultMinorVersion = 0;

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

        private static readonly string UpdateVersionFormat = "{0}.{1}";

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

        private static readonly string PackageNameFormat = "{0}{1}{2}";
        private static readonly string PackageEnvVarFormat = "{0}_{1}";
        #endregion

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

        #region Entry & Executing Assembly Data
        #region Entry Assembly Data
        private static readonly Assembly entryAssembly =
            Assembly.GetEntryAssembly();

        private static readonly AssemblyName entryAssemblyName =
            (entryAssembly != null) ? entryAssembly.GetName() : null;

        private static readonly string entryAssemblyTitle =
            AttributeOps.GetAssemblyTitle(entryAssembly);

        private static readonly string entryAssemblyLocation =
            (entryAssembly != null) ? entryAssembly.Location : null;

        private static readonly Version entryAssemblyVersion =
            (entryAssemblyName != null) ? entryAssemblyName.Version : null;

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

        private static readonly string entryAssemblyPath =
            AssemblyOps.GetPath(entryAssembly);
        #endregion

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

        #region Executing Assembly Data
        private static readonly Assembly thisAssembly =
            Assembly.GetExecutingAssembly();

#if CAS_POLICY
        private static readonly Evidence thisAssemblyEvidence =
            (thisAssembly != null) ? thisAssembly.Evidence : null;
#endif

        private static readonly AssemblyName thisAssemblyName =
            (thisAssembly != null) ? thisAssembly.GetName() : null;

        private static readonly string thisAssemblyTitle =
            AttributeOps.GetAssemblyTitle(thisAssembly);

        private static readonly string thisAssemblyLocation =
            (thisAssembly != null) ? thisAssembly.Location : null;

        private static readonly string thisAssemblySimpleName =
            (thisAssemblyName != null) ? thisAssemblyName.Name : null;

        private static readonly string thisAssemblyFullName =
            (thisAssemblyName != null) ? thisAssemblyName.FullName : null;

        private static readonly Version thisAssemblyVersion =
            (thisAssemblyName != null) ? thisAssemblyName.Version : null;

        private static readonly CultureInfo thisAssemblyCultureInfo =
            (thisAssemblyName != null) ? thisAssemblyName.CultureInfo : null;

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

        private static readonly string thisAssemblyPath =
            AssemblyOps.GetPath(thisAssembly);

        private static readonly Uri thisAssemblyUri =
            AttributeOps.GetAssemblyUri(thisAssembly);

        //
        // TODO: Change this if the update URI needs to be something
        //       other than the assembly URI.  In addition, the
        //       AttributeOps.GetAssemblyUpdateBaseUri method would
        //       most likely need to be changed as well.
        //
        private static readonly Uri thisAssemblyUpdateBaseUri =
            thisAssemblyUri;

        //
        // TODO: Change this if the XSD schema URI changes.
        //
        private static readonly Uri thisAssemblyNamespaceUri =
            (thisAssemblyUri != null) ?
                new Uri(thisAssemblyUri, "2009/schema") : null;

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

        #region Package Data
        //
        // NOTE: This is the base package name (e.g. "Eagle").
        //
        private static readonly string packageName = GetPackageName(false);

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

        //
        // NOTE: This is the base package name (e.g. "Eagle") in lower-case.
        //
        private static readonly string packageNameNoCase =
            GetPackageName(true);

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

        //
        // NOTE: This is the prefix (not including the trailing underscore)
        //       that is used when handling environment variables that are
        //       package-specific.
        //
        private static readonly string packageEnvVarPrefix = packageName;

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

        //
        // HACK: The package version *IS* the assembly version.
        //
        private static readonly Version packageVersion =
            thisAssemblyVersion;

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

#if UNIX
        //
        // NOTE: This is the base package name (e.g. "Eagle") in lower-case
        //       for use on Unix.
        //
        private static readonly string unixPackageName = packageNameNoCase;

        //
        // HACK: The Unix package version *IS* the assembly version.
        //
        private static readonly Version unixPackageVersion =
            thisAssemblyVersion;
#endif

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

#if DEBUGGER
        private static readonly string debuggerName =
            String.Format("{0} {1}", packageName, Debugger.Name).Trim();
#endif
        #endregion
        #endregion

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

        #region Binary Executable Data
        private static readonly string binaryPath = PathOps.GetBinaryPath(true);
        #endregion

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

        #region Resource Management Data
        private static readonly string resourceBaseName = packageName;
        #endregion
        #endregion
        #endregion

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

        #region Private Read-Only Primary Thread Data (Logical Constants)
        //
        // HACK: This is done dead last so we can output a useful diagnostic
        //       message showing that the global state has been initialized
        //       successfully.
        //
        private static readonly int primaryThreadId = GetCurrentThreadId();

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

        private static readonly Thread primaryThread =
            SetupPrimaryThread(primaryThreadId);
        #endregion

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

        #region Private Read-Write Data
        #region Diagnostic Data
        //
        // NOTE: When this is non-zero, diagnostic messages will be written
        //       whenever the simpler overloads of the GetEnvironmentVariable
        //       and SetEnvironmentVariable methods are called.
        //
        private static bool verboseEnvironment = Build.Debug &&
            (GetEnvironmentVariable(EnvVars.NoVerbose) == null);

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

#if POLICY_TRACE
        //
        // NOTE: When this is non-zero, policy trace diagnostics will always
        //       be written by the engine, regardless of the per-interpreter
        //       settings.
        //
        private static bool policyTrace = false;
#endif
        #endregion

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

        #region Object Identity Data
#if RANDOMIZE_ID
        private static Random random = new Random();
        private static long nextId = Math.Abs(random.Next());

#if !SHARED_ID_POOL
        private static long nextInterpreterId = Math.Abs(random.Next());
#endif
#else
        private static long nextId = 0;

#if !SHARED_ID_POOL
        private static long nextInterpreterId = 0;
#endif
#endif
        #endregion

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

        #region Threading Data
#if false && NATIVE && WINDOWS
        private static bool getNativeThreadId;
#endif

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

        [ThreadStatic()] /* ThreadSpecificData */
        private static int currentThreadId;
        #endregion

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

        #region "Active" / "All" Interpreter Tracking
        [ThreadStatic()] /* ThreadSpecificData */
        private static InterpreterStackList activeInterpreters;

        private static readonly InterpreterDictionary allInterpreters =
            new InterpreterDictionary();
        #endregion

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

        #region Path Override Data
        #region Environment Variable Path Overrides
        private static string libraryPath =
            GetEnvironmentVariable(EnvVars.EagleLibrary, false, false);

        private static StringList autoPathList = StringList.FromString(
            GetEnvironmentVariable(EnvVars.EagleLibPath, false, false));

        private static string tclLibraryPath =
            GetEnvironmentVariable(EnvVars.TclLibrary, true, false);

        private static StringList tclAutoPathList = StringList.FromString(
            GetEnvironmentVariable(EnvVars.TclLibPath, true, false));
        #endregion

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

        #region Master Base / Library Path Overrides
        //
        // NOTE: Master override for the base path.
        //
        private static string masterBasePath = null;

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

        //
        // NOTE: Master override for the library path.
        //
        private static string masterLibraryPath = null;
        #endregion
        #endregion

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

        #region Package Path Data
        private static string assemblyPackageNamePath =
            GetAssemblyPackagePath(packageName, packageVersion);

        private static string assemblyPackageRootPath =
            GetAssemblyPackagePath(null, null);

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

        private static string binaryPackageNamePath =
            GetBinaryPackagePath(packageName, packageVersion);

        private static string binaryPackageRootPath =
            GetBinaryPackagePath(null, null);

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

        private static string packagePeerPath = GetPackagePath(
            thisAssembly, null, null, false);

        private static string packageRootPath = GetPackagePath(
            thisAssembly, null, null, true);

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

        private static string packageNamePath = GetPackagePath(
            thisAssembly, packageName, packageVersion, false);

        private static string packageNameRootPath = GetPackagePath(
            thisAssembly, packageName, packageVersion, true);

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

#if UNIX
        private static string unixPackageNameLocalPath = GetUnixPackagePath(
            unixPackageName, unixPackageVersion, true);

        private static string unixPackageNamePath = GetUnixPackagePath(
            unixPackageName, unixPackageVersion, false);
#endif

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

#if NATIVE && TCL
        private static string tclPackageNamePath =
            GetPackagePath(thisAssembly, TclVars.PackageName, null, false);

        private static string tclPackageNameRootPath =
            GetPackagePath(thisAssembly, TclVars.PackageName, null, true);
#endif
        #endregion

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

        #region Master Auto-Path List
        //
        // NOTE: Master auto-path list.  This list is automatically initialized
        //       [once] when necessary; however, it may then be overridden to
        //       influence the behavior of any interpreters created after it
        //       has been changed.
        //
        private static StringList masterAutoPathList = null;
        #endregion
        #endregion

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

        #region Object Identity Methods
        public static long NextId() /* THREAD-SAFE */
        {
            long result;

            //
            // NOTE: This is our cheap unique Id generator for
            //       the various script visible identifiers
            //       (such as channel names, etc).  This value
            //       is not per-interpreter; therefore, use with
            //       caution.
            //
            result = Interlocked.Increment(ref nextId);

            if (result < 0)
                DebugOps.Complain(ReturnCode.Error,
                    "next global identifier is negative");

            return result;
        }

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

        public static long NextId(
            Interpreter interpreter
            ) /* THREAD-SAFE */
        {
            return (interpreter != null) ?
                interpreter.NextId() : NextId();
        }

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

        public static long NextInterpreterId() /* THREAD-SAFE */
        {
            long result;

            //
            // NOTE: Interpreter names should be totally unique within the
            //       application domain (or the process?); therefore, this
            //       must be global.
            //
#if SHARED_ID_POOL
            result = NextId();
#else
            result = Interlocked.Increment(ref nextInterpreterId);
#endif

            if (result < 0)
                DebugOps.Complain(ReturnCode.Error,
                    "next interpreter identifier is negative");

            return result;
        }

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

        public static long NextEventId(
            Interpreter interpreter
            ) /* THREAD-SAFE */
        {
            //
            // NOTE: Event names must be totally unique within the application
            //       domain (or the process?); therefore, this must be global.
            //
            return NextId();
        }

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

        public static long NextTypeId(
            Interpreter interpreter
            ) /* THREAD-SAFE */
        {
            //
            // NOTE: Type names must be totally unique within the application
            //       domain; therefore, this must be global.
            //
            return NextId();
        }

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

        public static long NextThreadId(
            Interpreter interpreter
            ) /* THREAD-SAFE */
        {
            //
            // NOTE: Thread names should be totally unique within the process;
            //       therefore, this must be global.
            //
            return NextId();
        }
        #endregion

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

        #region Threading Global Variable Access Methods
        public static int GetCurrentThreadId() /* THREAD-SAFE */
        {
#if false && NATIVE && WINDOWS
            //
            // NOTE: *PERF* Thread-local storage is really slow for storing
            //       values needed in the hot-paths of the context manager;
            //       therefore, avoid it if at all possible.
            //
            if (getNativeThreadId)
                return NativeOps.SafeNativeMethods.GetCurrentThreadId();
#endif

            int threadId = currentThreadId;

            if (threadId == 0)
                currentThreadId = threadId = AppDomain.GetCurrentThreadId();

            return threadId;
        }

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

        private static Thread SetupPrimaryThread(
            int threadId
            ) /* THREAD-SAFE */
        {
#if false && NATIVE && WINDOWS
            PlatformOps.Initialize();

            getNativeThreadId = PlatformOps.IsWindowsOperatingSystem();
#endif

            Thread thread = Thread.CurrentThread;
            string threadName = FormatOps.DisplayThread(thread);

            TraceOps.DebugTrace(String.Format(
                "{0}: SetupPrimaryThread: library initialized on managed " +
                "thread with [{1}] and with next object Id {2} and with " +
                "next interpreter Id {3}.{4}", threadId,
                threadName, nextId, nextInterpreterId, Environment.NewLine),
                typeof(GlobalState).Name);

            return thread;
        }

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

        public static int GetPrimaryThreadId() /* THREAD-SAFE */
        {
            return primaryThreadId;
        }

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

        public static Thread GetPrimaryThread() /* THREAD-SAFE */
        {
            return primaryThread;
        }

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

        public static bool IsPrimaryThread() /* THREAD-SAFE */
        {
            return IsPrimaryThread(GetCurrentThreadId());
        }

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

        private static bool IsPrimaryThread(
            int threadId
            ) /* THREAD-SAFE */
        {
            return (threadId == GetPrimaryThreadId());
        }
        #endregion

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

        #region "Active" / "All" Interpreter Tracking Methods
        #region "All" Interpreter Tracking Methods
        public static ReturnCode GetInterpreter( /* NOTE: GetAnyInterpreter */
            LookupFlags lookupFlags,
            ref Interpreter interpreter,
            ref Result error
            ) /* THREAD-SAFE */
        {
            lock (syncRoot)
            {
                if (allInterpreters != null)
                {
                    //
                    // NOTE: Grab the first available [and valid?] interpreter.
                    //
                    bool validate = FlagOps.HasFlags(
                        lookupFlags, LookupFlags.Validate, true);

                    foreach (KeyValuePair<string, Interpreter> pair
                            in allInterpreters)
                    {
                        interpreter = pair.Value;

                        if (!validate || (interpreter != null))
                            return ReturnCode.Ok;
                    }

                    error = FlagOps.HasFlags(
                        lookupFlags, LookupFlags.Verbose, true) ?
                        String.Format(
                            "no {0}interpreter found",
                            validate ? "valid " : String.Empty) :
                        "no interpreter found";
                }
                else
                {
                    error = "no interpreters available";
                }
            }

            return ReturnCode.Error;
        }

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

        public static ReturnCode GetInterpreter(
            string name,
            LookupFlags lookupFlags,
            ref Interpreter interpreter,
            ref Result error
            ) /* THREAD-SAFE */
        {
            lock (syncRoot)
            {
                if (allInterpreters != null)
                {
                    //
                    // NOTE: *WARNING* Empty interpreter names are
                    //       technically allowed, please do not
                    //       change this to "!String.IsNullOrEmpty".
                    //
                    if (name != null)
                    {
                        if (allInterpreters.TryGetValue(name, out interpreter))
                        {
                            if ((interpreter != null) ||
                                !FlagOps.HasFlags(
                                    lookupFlags, LookupFlags.Validate, true))
                            {
                                return ReturnCode.Ok;
                            }
                            else
                            {
                                error = FlagOps.HasFlags(
                                    lookupFlags, LookupFlags.Verbose, true) ?
                                    String.Format(
                                        "invalid interpreter name \"{0}\"",
                                        FormatOps.DisplayName(name)) :
                                    "invalid interpreter name";
                            }
                        }
                        else
                        {
                            error = FlagOps.HasFlags(
                                lookupFlags, LookupFlags.Verbose, true) ?
                                String.Format(
                                    "interpreter \"{0}\" not found",
                                    FormatOps.DisplayName(name)) :
                                "interpreter not found";
                        }
                    }
                    else
                    {
                        error = "invalid interpreter name";
                    }
                }
                else
                {
                    error = "no interpreters available";
                }
            }

            return ReturnCode.Error;
        }

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

        public static string InterpretersToString(
            string pattern,
            bool noCase
            ) /* THREAD-SAFE */
        {
            lock (syncRoot)
            {
                if (allInterpreters != null)
                    return allInterpreters.ToString(pattern, noCase);
                else
                    return null;
            }
        }

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

        public static InterpreterDictionary GetInterpreters() /* THREAD-SAFE */
        {
            lock (syncRoot)
            {
                if (allInterpreters != null)
                    return (InterpreterDictionary)allInterpreters.Clone();
            }

            return null;
        }

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

        public static IEnumerable<IInterpreter> FilterInterpreters(
            IEnumerable<IInterpreter> interpreters,
            bool found,
            bool nonPrimary
            ) /* THREAD-SAFE */
        {
            if (interpreters == null)
                return null;

            lock (syncRoot)
            {
                if (allInterpreters != null)
                {
                    IList<IInterpreter> result = new List<IInterpreter>();

                    foreach (IInterpreter interpreter in interpreters)
                    {
                        Interpreter value = interpreter as Interpreter;

                        if (allInterpreters.ContainsValue(value) == found)
                            result.Add(value);
                        else if (nonPrimary && !value.IsPrimaryThread())
                            result.Add(value);
                    }

                    return result;
                }
            }

            return null;
        }

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

        public static bool AddInterpreter(
            Interpreter interpreter
            ) /* THREAD-SAFE */
        {
            lock (syncRoot)
            {
                if (allInterpreters != null)
                {
                    allInterpreters.Add(interpreter);

                    return true;
                }
            }

            return false;
        }

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

        public static bool RemoveInterpreter(
            Interpreter interpreter
            ) /* THREAD-SAFE */
        {
            lock (syncRoot)
            {
                if (allInterpreters != null)
                    return allInterpreters.Remove(interpreter);
            }

            return false;
        }
        #endregion

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

        #region "Active" Interpreter Tracking Methods
        public static bool IsActiveInterpreter(
            Interpreter interpreter
            ) /* THREAD-SAFE */
        {
            if (activeInterpreters == null)
                return false;

            return activeInterpreters.ContainsInterpreter(interpreter);
        }

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

        /* THREAD-SAFE */
        private static IAnyPair<Interpreter, IClientData> GetActiveInterpreter()
        {
            return GetActiveInterpreter(null);
        }

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

        public static IAnyPair<Interpreter, IClientData> GetActiveInterpreter(
            Type type
            ) /* THREAD-SAFE */
        {
            if ((activeInterpreters != null) && !activeInterpreters.IsEmpty)
            {
                if (type != null)
                {
                    for (int index = 0;
                            index < activeInterpreters.Count;
                            index++)
                    {
                        IAnyPair<Interpreter, IClientData> pair =
                            activeInterpreters.Peek(index);

                        if (pair == null)
                            continue;

                        IClientData clientData = pair.Y;

                        if (clientData == null)
                            continue;

                        if (clientData.GetType() == type)
                            return pair;
                    }
                }
                else
                {
                    return activeInterpreters.Peek();
                }
            }

            return null;
        }

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

        public static string ActiveInterpretersToString(
            string pattern,
            bool noCase
            ) /* THREAD-SAFE */
        {
            if (activeInterpreters != null)
                return activeInterpreters.ToString(pattern, noCase);
            else
                return null;
        }

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

        /* THREAD-SAFE */
        private static InterpreterStackList GetActiveInterpreters()
        {
            if (activeInterpreters != null)
                return (InterpreterStackList)activeInterpreters.Clone();

            return null;
        }

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

        public static IEnumerable<IInterpreter> FilterActiveInterpreters(
            IEnumerable<IInterpreter> interpreters,
            bool found,
            bool nonPrimary
            ) /* THREAD-SAFE */
        {
            if (interpreters == null)
                return null;

            if (activeInterpreters != null)
            {
                IList<IInterpreter> result = new List<IInterpreter>();

                foreach (IInterpreter interpreter in interpreters)
                {
                    Interpreter value = interpreter as Interpreter;

                    if (activeInterpreters.ContainsInterpreter(value) == found)
                        result.Add(value);
                    else if (nonPrimary && !value.IsPrimaryThread())
                        result.Add(value);
                }

                return result;
            }

            return null;
        }

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

        public static void PushActiveInterpreter(
            Interpreter interpreter
            ) /* THREAD-SAFE */
        {
            PushActiveInterpreter(interpreter, null);
        }

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

        public static void PushActiveInterpreter(
            Interpreter interpreter,
            IClientData clientData
            ) /* THREAD-SAFE */
        {
            int pushed = 0;

            PushActiveInterpreter(interpreter, clientData, ref pushed);
        }

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

        private static void PushActiveInterpreter(
            Interpreter interpreter,
            IClientData clientData,
            ref int pushed
            ) /* THREAD-SAFE */
        {
            if (activeInterpreters == null)
                activeInterpreters = new InterpreterStackList();

            activeInterpreters.Push(new AnyPair<Interpreter, IClientData>(
                interpreter, clientData));

            Interlocked.Increment(ref pushed);

            ///////////////////////////////////////////////////////////////////
            // BEGIN NOT THREAD-SAFE
            ///////////////////////////////////////////////////////////////////

#if NOTIFY && NOTIFY_GLOBAL && NOTIFY_ACTIVE
            if ((interpreter != null) && !interpreter.Disposed &&
                interpreter.GlobalNotify)
            {
                /* IGNORED */
                Interpreter.CheckNotifications(null, false,
                    NotifyType.Interpreter, NotifyFlags.Pushed,
                    null, interpreter, clientData, null, null);
            }
#endif

            ///////////////////////////////////////////////////////////////////
            // END NOT THREAD-SAFE
            ///////////////////////////////////////////////////////////////////
        }

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

        /* THREAD-SAFE */
        public static IAnyPair<Interpreter, IClientData> PopActiveInterpreter()
        {
            int pushed = 1; // required, or no pop.

            return PopActiveInterpreter(ref pushed);
        }

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

        private static IAnyPair<Interpreter, IClientData> PopActiveInterpreter(
            ref int pushed
            ) /* THREAD-SAFE */
        {
            IAnyPair<Interpreter, IClientData> pair = null;

            if (pushed > 0)
            {
                if ((activeInterpreters != null) &&
                    !activeInterpreters.IsEmpty)
                {
                    pair = activeInterpreters.Pop();
                    Interlocked.Decrement(ref pushed);
                }
            }

            ///////////////////////////////////////////////////////////////////
            // BEGIN NOT THREAD-SAFE
            ///////////////////////////////////////////////////////////////////

#if NOTIFY && NOTIFY_GLOBAL && NOTIFY_ACTIVE
            if (pair != null)
            {
                Interpreter interpreter = pair.X;

                if ((interpreter != null) && !interpreter.Disposed &&
                    interpreter.GlobalNotify)
                {
                    IClientData clientData = pair.Y;

                    /* IGNORED */
                    Interpreter.CheckNotifications(null, false,
                        NotifyType.Interpreter, NotifyFlags.Popped,
                        null, interpreter, clientData, null, null);
                }
            }
#endif

            ///////////////////////////////////////////////////////////////////
            // END NOT THREAD-SAFE
            ///////////////////////////////////////////////////////////////////

            return pair;
        }
        #endregion
        #endregion

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

        #region Policy Trace Support Properties
#if POLICY_TRACE
        public static bool PolicyTrace /* THREAD-SAFE */
        {
            get
            {
                lock (syncRoot)
                {
                    return policyTrace;
                }
            }
            set
            {
                lock (syncRoot)
                {
                    policyTrace = value;
                }
            }
        }
#endif
        #endregion

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

        #region Environment Variable Management Methods
        private static string GetEnvironmentVariable(
            string variable
            ) /* THREAD-SAFE */
        {
            return CommonOps.Environment.GetVariable(variable);
        }

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

        private static string ExpandEnvironmentVariables(
            string name
            ) /* THREAD-SAFE */
        {
            return CommonOps.Environment.ExpandVariables(name);
        }

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

        public static string GetEnvironmentVariable(
            string variable,
            bool prefix,
            bool expand
            ) /* THREAD-SAFE */
        {
            return GetEnvironmentVariable(
                variable, prefix, expand, verboseEnvironment);
        }

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

        public static string GetEnvironmentVariable(
            string variable,
            bool prefix,
            bool expand,
            bool verbose
            ) /* THREAD-SAFE */
        {
            string value = null;

            //
            // NOTE: If the variable name is null or empty, return the default
            //       value (null) instead of potentially throwing an exception
            //       later.
            //
            if (String.IsNullOrEmpty(variable))
                return value;

            //
            // NOTE: Try the variable name prefixed by our package name first?
            //
            if (prefix)
            {
                value = GetEnvironmentVariable(String.Format(
                    PackageEnvVarFormat, packageEnvVarPrefix, variable));
            }

            //
            // NOTE: Failing to find that, just try for the variable name.
            //
            if (value == null)
                value = GetEnvironmentVariable(variable);

            //
            // NOTE: If necessary, expand any contained environment variables.
            //
            if (expand && !String.IsNullOrEmpty(value))
                value = ExpandEnvironmentVariables(value);

            //
            // NOTE: Output a diagnostic message about the environment variable
            //       request.
            //
            if (verbose)
                TraceOps.DebugTrace(String.Format(
                    "{0}: GetEnvironmentVariable: variable = {1}, " +
                    "prefix = {2}, expand = {3}, value = {4}{5}",
                    GetCurrentThreadId(), FormatOps.WrapOrNull(variable),
                    prefix, expand, FormatOps.WrapOrNull(value),
                    Environment.NewLine), typeof(GlobalState).Name);

            return value;
        }

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

        public static void SetEnvironmentVariable(
            string variable,
            string value,
            bool prefix
            ) /* THREAD-SAFE */
        {
            SetEnvironmentVariable(
                variable, value, prefix, verboseEnvironment);
        }

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

        private static void SetEnvironmentVariable(
            string variable,
            string value,
            bool prefix,
            bool verbose
            ) /* THREAD-SAFE */
        {
            //
            // NOTE: If the variable name is null or empty, do nothing.
            //
            if (String.IsNullOrEmpty(variable))
                return;

            //
            // NOTE: Set the variable name prefixed by package name instead?
            //
            if (prefix)
            {
                variable = String.Format(
                    PackageEnvVarFormat, packageEnvVarPrefix, variable);
            }

            //
            // NOTE: Attempt to set the requested environment variable.
            //
            CommonOps.Environment.SetVariable(variable, value);

            //
            // NOTE: Output diagnostic message about the environment variable
            //       request.
            //
            if (verbose)
                TraceOps.DebugTrace(String.Format(
                    "{0}: SetEnvironmentVariable: variable = {1}, " +
                    "value = {2}, prefix = {3}{4}", GetCurrentThreadId(),
                    FormatOps.WrapOrNull(variable), FormatOps.WrapOrNull(value),
                    prefix, Environment.NewLine), typeof(GlobalState).Name);
        }
        #endregion

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

        #region Assembly Global Variable Access Methods
        #region Entry Assembly Methods
        public static bool IsEntryAssembly(
            Assembly assembly
            ) /* THREAD-SAFE */
        {
            return Object.ReferenceEquals(assembly, entryAssembly);
        }

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

        public static Assembly GetEntryAssembly() /* THREAD-SAFE */
        {
            return entryAssembly;
        }

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

        private static AssemblyName GetEntryAssemblyName() /* THREAD-SAFE */
        {
            return entryAssemblyName;
        }

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

        private static string GetEntryAssemblyTitle() /* THREAD-SAFE */
        {
            return entryAssemblyTitle;
        }

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

        public static string GetEntryAssemblyLocation() /* THREAD-SAFE */
        {
            return entryAssemblyLocation;
        }

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

        public static Version GetEntryAssemblyVersion() /* THREAD-SAFE */
        {
            return entryAssemblyVersion;
        }

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

        public static string GetEntryAssemblyPath() /* THREAD-SAFE */
        {
            return entryAssemblyPath;
        }
        #endregion

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

        #region Executing Assembly Methods
        public static bool IsAssembly(
            Assembly assembly
            ) /* THREAD-SAFE */
        {
            return Object.ReferenceEquals(assembly, thisAssembly);
        }

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

        public static Assembly GetAssembly() /* THREAD-SAFE */
        {
            return thisAssembly;
        }

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

#if CAS_POLICY
        public static Evidence GetAssemblyEvidence() /* THREAD-SAFE */
        {
            return thisAssemblyEvidence;
        }
#endif

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

        public static AssemblyName GetAssemblyName() /* THREAD-SAFE */
        {
            return thisAssemblyName;
        }

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

        public static string GetAssemblyTitle() /* THREAD-SAFE */
        {
            return thisAssemblyTitle;
        }

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

        public static string GetAssemblyLocation() /* THREAD-SAFE */
        {
            return thisAssemblyLocation;
        }

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

        public static string GetAssemblySimpleName() /* THREAD-SAFE */
        {
            return thisAssemblySimpleName;
        }

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

        public static string GetAssemblyFullName() /* THREAD-SAFE */
        {
            return thisAssemblyFullName;
        }

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

        public static Version GetAssemblyVersion() /* THREAD-SAFE */
        {
            return thisAssemblyVersion;
        }

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

        public static string GetAssemblyUpdateVersion() /* THREAD-SAFE */
        {
            if (thisAssemblyVersion == null)
                return null;

            if ((thisAssemblyVersion.Major == DefaultMajorVersion) &&
                (thisAssemblyVersion.Minor == DefaultMinorVersion))
            {
                //
                // NOTE: This has a default major and minor version, use
                //       the build and revision only.
                //
                return String.Format(
                    UpdateVersionFormat, thisAssemblyVersion.Build,
                    thisAssemblyVersion.Revision);
            }
            else
            {
                //
                // NOTE: This has a non-default major or minor version,
                //       use the full version string.
                //
                return thisAssemblyVersion.ToString();
            }
        }

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

        public static CultureInfo GetAssemblyCultureInfo() /* THREAD-SAFE */
        {
            return thisAssemblyCultureInfo;
        }

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

        public static string GetAssemblyPath() /* THREAD-SAFE */
        {
            return thisAssemblyPath;
        }

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

        public static Uri GetAssemblyUri() /* THREAD-SAFE */
        {
            return thisAssemblyUri;
        }

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

        public static Uri GetAssemblyUpdateBaseUri() /* THREAD-SAFE */
        {
            return thisAssemblyUpdateBaseUri;
        }

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

        public static Uri GetAssemblyNamespaceUri() /* THREAD-SAFE */
        {
            return thisAssemblyNamespaceUri;
        }
        #endregion

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

        #region Binary Executable Methods
        public static string GetBinaryPath() /* THREAD-SAFE */
        {
            return binaryPath;
        }

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

        private static bool HaveBinaryPath() /* THREAD-SAFE */
        {
            return !String.IsNullOrEmpty(binaryPath);
        }
        #endregion

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

        #region Resource Management Methods
        public static string GetResourceBaseName() /* THREAD-SAFE */
        {
            return resourceBaseName;
        }
        #endregion
        #endregion

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

        #region Package Global Variable Access Methods
#if DEBUGGER
        public static string GetDebuggerName() /* THREAD-SAFE */
        {
            return debuggerName;
        }
#endif

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

        /* THREAD-SAFE */
        public static string GetPackageName() /* CANNOT RETURN NULL */
        {
            return packageName;
        }

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

        /* THREAD-SAFE */
        public static string GetPackageNameNoCase() /* CANNOT RETURN NULL */
        {
            return packageNameNoCase;
        }

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

        public static string GetPackageName( /* CANNOT RETURN NULL */
            bool noCase
            ) /* THREAD-SAFE */
        {
            return GetPackageName(null, null, noCase);
        }

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

        public static string GetPackageName( /* CANNOT RETURN NULL */
            string prefix,
            string suffix,
            bool noCase
            ) /* THREAD-SAFE */
        {
            //
            // NOTE: We do not want to return the assembly name here because
            //       that is not guaranteed to be the same as what we consider
            //       the "package name".
            //
            string result = GetAssemblyTitle();

            if (String.IsNullOrEmpty(result))
                result = DefaultPackageName;

            if (noCase)
                result = result.ToLowerInvariant();

            if (!String.IsNullOrEmpty(prefix) || !String.IsNullOrEmpty(suffix))
            {
                result = String.Format(
                    PackageNameFormat, prefix, result, suffix);
            }

            return result;
        }

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

        private static string GetPackageNameRootPath() /* THREAD-SAFE */
        {
            lock (syncRoot)
            {
                return packageNameRootPath;
            }
        }

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

        public static Version GetPackageVersion() /* THREAD-SAFE */
        {
            lock (syncRoot)
            {
                //
                // NOTE: Package versions do not typically include the build
                //       and revision numbers; therefore, be sure they are
                //       omitted in our return value.
                //
                return (packageVersion != null) ?
                    new Version(packageVersion.Major,
                        packageVersion.Minor) :
                    new Version(DefaultMajorVersion, DefaultMinorVersion);
            }
        }

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

#if NATIVE && TCL
        public static string GetTclPackageNamePath() /* THREAD-SAFE */
        {
            lock (syncRoot)
            {
                return tclPackageNamePath;
            }
        }

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

        public static string GetTclPackageNameRootPath() /* THREAD-SAFE */
        {
            lock (syncRoot)
            {
                return tclPackageNameRootPath;
            }
        }
#endif
        #endregion

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

        #region Raw Binary Base Path Management Methods
        public static string GetRawBinaryBasePath() /* THREAD-SAFE */
        {
            return GetRawBinaryBasePath(thisAssembly);
        }

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

        private static string GetRawBinaryBasePath(
            Assembly assembly /* OPTIONAL: May be null. */
            ) /* THREAD-SAFE */
        {
            return GetRawBasePath(assembly, GetBinaryPath());
        }
        #endregion

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

        #region Raw Base Path Management Methods
        private static string GetRawBasePath(
            string path
            ) /* THREAD-SAFE */
        {
            return GetRawBasePath(thisAssembly, path);
        }

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

        private static string GetRawBasePath(
            Assembly assembly, /* OPTIONAL: May be null. */
            string path
            ) /* THREAD-SAFE */
        {
            return PathOps.GetBasePath(assembly, path);
        }
        #endregion

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

        #region Base Path Global Variable Management Methods
        public static string GetBasePath() /* THREAD-SAFE */
        {
            return GetBasePath(thisAssembly);
        }

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

        private static string GetBasePath(
            Assembly assembly /* OPTIONAL: May be null. */
            ) /* THREAD-SAFE */
        {
            string result = null;

            lock (syncRoot)
            {
                //
                // NOTE: Allow manual override of the base path via the
                //       SetBasePath method.
                //
                result = masterBasePath;

                //
                // NOTE: Was the master base path set to something that
                //       looks valid?
                //
                if (String.IsNullOrEmpty(result))
                {
                    //
                    // NOTE: Allow the "EAGLE_BASE" environment variable
                    //       to override the base path.
                    //
                    result = GetEnvironmentVariable(EnvVars.EagleBase);
                }

                //
                // NOTE: Was the "EAGLE_BASE" environment variable set to
                //       something that looks valid?
                //
                if (String.IsNullOrEmpty(result))
                {
                    //
                    // NOTE: Check if the assembly specified by the caller,
                    //       if any, is present in the GAC.
                    //
                    if ((assembly != null) && assembly.GlobalAssemblyCache)
                    {
                        //
                        // NOTE: The specified assembly has been GAC'd.  We
                        //       need to use the registry to find where we
                        //       were actually installed to.
                        //
                        result = SetupOps.GetPath(packageVersion);

                        //
                        // NOTE: If we failed to get the path from the setup
                        //       registry hive (perhaps setup was not run?)
                        //       then we resort to using the current assembly
                        //       probing path for the application domain, if
                        //       possible.
                        //
                        if (String.IsNullOrEmpty(result) && HaveBinaryPath())
                            result = GetRawBinaryBasePath(assembly);
                    }
                    else
                    {
                        //
                        // NOTE: Return the base directory that this assembly
                        //       was loaded from (i.e. without the "bin"), if
                        //       possible.
                        //
                        if (HaveBinaryPath())
                            result = GetRawBinaryBasePath(assembly);
                    }
                }
            }

            return result;
        }

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

        public static void SetBasePath( /* EXTERNAL USE ONLY */
            string basePath,
            bool refresh
            ) /* THREAD-SAFE */
        {
            lock (syncRoot)
            {
                masterBasePath = basePath;

                //
                // BUGFIX: Be sure to propagate the changes down to where they
                //         are actually useful.
                //
                if (refresh)
                    RefreshBasePath();
            }
        }

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

        private static void RefreshBasePath() /* THREAD-SAFE */
        {
            RefreshLibraryPath();
        }
        #endregion

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

        #region Library Path Global Variable Management Methods
        public static string GetLibraryPath(
            Interpreter interpreter /* OPTIONAL: May be null. */
            ) /* THREAD-SAFE */
        {
            return GetLibraryPath(interpreter, (interpreter != null) ?
                interpreter.CreateFlags : CreateFlags.None);
        }

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

        private static string GetLibraryPath(
            Interpreter interpreter, /* OPTIONAL: May be null. */
            CreateFlags createFlags
            ) /* THREAD-SAFE */
        {
            bool showAutoPath = FlagOps.HasFlags(
                createFlags, CreateFlags.ShowAutoPath, true);

            bool strictAutoPath = FlagOps.HasFlags(
                createFlags, CreateFlags.StrictAutoPath, true);

            PathDictionary<object> paths = null;

            GetInterpreterAutoPathList(
                interpreter, true, showAutoPath, strictAutoPath,
                ref paths);

            GetMasterAutoPathList(
                interpreter, true, showAutoPath, strictAutoPath,
                ref paths);

            if ((paths != null) && (paths.Count > 0))
            {
                string path = new StringList(paths.Keys)[0]; /* HACK */

                if (!String.IsNullOrEmpty(path))
                    return path;
            }

            return GetPackageNameRootPath(); // COMPAT: Eagle beta.
        }

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

        private static string GetLibraryPath(
            Assembly assembly, /* OPTIONAL: May be null. */
            bool root
            ) /* THREAD-SAFE */
        {
            lock (syncRoot)
            {
                if (!String.IsNullOrEmpty(masterLibraryPath))
                {
                    //
                    // NOTE: Allow manual override of the library path.
                    //
                    return masterLibraryPath;
                }
                else if (root)
                {
                    //
                    // NOTE: We want the root library directory.  This path
                    //       allows us to run from the build directory (e.g.
                    //       "bin\Debug\bin") and still refer to directories
                    //       that are not in the build directory (i.e. they
                    //       are a peer of the outer "bin" directory).
                    //
                    return Path.GetFullPath(PathOps.CombinePath(
                        null, GetBasePath(assembly), TclVars.LibPath));
                }
                else
                {
                    //
                    // NOTE: We want the non-root (or peer) library directory.
                    //       When running in a non-build environment, this will
                    //       typically be the same as the root library directory.
                    //
                    // BUGBUG: This basically assumes that the directory
                    //         containing the application binary is parallel to
                    //         the Eagle library directory.  This will not work
                    //         if Eagle is running from "/usr/local/bin/Eagle"
                    //         and the library directory is "/usr/local/lib/Eagle/"
                    //         (OpenBSD).  In order for that layout to work, we
                    //         would have to go up one more level.
                    //
                    return Path.GetFullPath(PathOps.CombinePath(
                        null, Path.GetDirectoryName(GetBinaryPath()),
                        TclVars.LibPath));
                }
            }
        }

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

        public static void SetLibraryPath(
            string libraryPath,
            bool refresh
            ) /* THREAD-SAFE */
        {
            lock (syncRoot)
            {
                masterLibraryPath = libraryPath;

                //
                // BUGFIX: Be sure to propagate the changes down to where they
                //         are actually useful.
                //
                if (refresh)
                    RefreshLibraryPath();
            }
        }

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

#if UNIX
        public static string GetUnixLibraryPath(
            bool local
            ) /* THREAD-SAFE */
        {
            lock (syncRoot)
            {
                if (!String.IsNullOrEmpty(masterLibraryPath))
                {
                    //
                    // NOTE: Allow manual override of the library path.
                    //
                    return masterLibraryPath;
                }
                else if (local)
                {
                    //
                    // NOTE: We want the directory where local libraries
                    //       are installed.
                    //
                    return TclVars.UserLocalLibPath;
                }
                else
                {
                    //
                    // NOTE: We want the directory where libraries are
                    //       installed.
                    //
                    return TclVars.UserLibPath;
                }
            }
        }
#endif

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

        private static void RefreshLibraryPath() /* THREAD-SAFE */
        {
            lock (syncRoot)
            {
                packagePeerPath = GetPackagePath(
                    thisAssembly, null, null, false);

                packageRootPath = GetPackagePath(
                    thisAssembly, null, null, true);

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

                packageNamePath = GetPackagePath(
                    thisAssembly, packageName, packageVersion, false);

                packageNameRootPath = GetPackagePath(
                    thisAssembly, packageName, packageVersion, true);

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

#if UNIX
                unixPackageNameLocalPath = GetUnixPackagePath(
                    unixPackageName, unixPackageVersion, true);

                unixPackageNamePath = GetUnixPackagePath(
                    unixPackageName, unixPackageVersion, false);
#endif

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

#if NATIVE && TCL
                tclPackageNamePath = GetPackagePath(
                    thisAssembly, TclVars.PackageName, null, false);

                tclPackageNameRootPath = GetPackagePath(
                    thisAssembly, TclVars.PackageName, null, true);
#endif

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

                //
                // BUGFIX: Reset the master auto-path so that it will be
                //         initialized again [using our new paths] on the
                //         next call to the GetAutoPathList method.
                //
                ResetMasterAutoPathList();
            }
        }
        #endregion

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

        #region Package Path Global Variable Management Methods
        public static string GetPackagePath(
            Assembly assembly, /* OPTIONAL: May be null. */
            string name,       /* OPTIONAL: May be null. */
            Version version,   /* OPTIONAL: May be null. */
            bool root
            ) /* THREAD-SAFE */
        {
            string result = GetLibraryPath(assembly, root);

            if (!String.IsNullOrEmpty(name))
            {
                result = PathOps.CombinePath(null, result, name);

                if (version != null)
                    result += FormatOps.MajorMinor(version);
            }

            return result;
        }

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

#if UNIX
        public static string GetUnixPackagePath(
            string name,
            Version version,
            bool local
            ) /* THREAD-SAFE */
        {
            string result = GetUnixLibraryPath(local);

            if (!String.IsNullOrEmpty(name))
            {
                result = PathOps.CombinePath(null, result, name);

                if (version != null)
                    result += FormatOps.MajorMinor(version);
            }

            return result;
        }
#endif

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

        public static string GetAssemblyPackagePath(
            string name,
            Version version
            ) /* THREAD-SAFE */
        {
            string result = null;

            if (!String.IsNullOrEmpty(thisAssemblyPath))
            {
                string basePath = GetRawBasePath(thisAssemblyPath);

                if (!String.IsNullOrEmpty(basePath))
                {
                    result = PathOps.CombinePath(
                        null, basePath, TclVars.LibPath);

                    if (!String.IsNullOrEmpty(name))
                    {
                        result = PathOps.CombinePath(null, result, name);

                        if (version != null)
                            result += FormatOps.MajorMinor(version);
                    }
                }
            }

            return result;
        }

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

        private static string GetBinaryPackagePath(
            string name,
            Version version
            ) /* THREAD-SAFE */
        {
            string result = null;

            if (HaveBinaryPath())
            {
                string basePath = GetRawBinaryBasePath();

                if (!String.IsNullOrEmpty(basePath))
                {
                    result = PathOps.CombinePath(
                        null, basePath, TclVars.LibPath);

                    if (!String.IsNullOrEmpty(name))
                    {
                        result = PathOps.CombinePath(null, result, name);

                        if (version != null)
                            result += FormatOps.MajorMinor(version);
                    }
                }
            }

            return result;
        }
        #endregion

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

        #region Auto-Path Support Methods
        private static void GetInterpreterAutoPathList(
            Interpreter interpreter,
            bool libraryOnly,
            bool showAutoPath,
            bool strictAutoPath,
            ref PathDictionary<object> paths
            ) /* THREAD-SAFE */
        {
            if (interpreter == null)
                return;

            lock (interpreter.SyncRoot)
            {
                if (paths == null)
                    paths = new PathDictionary<object>();

                string libraryPath = interpreter.LibraryPath; /* throw */

                if (!String.IsNullOrEmpty(libraryPath) &&
                    !paths.ContainsKey(libraryPath) &&
                    (!strictAutoPath || Directory.Exists(libraryPath)))
                {
                    paths.Add(libraryPath, null);

                    if (showAutoPath)
                    {
                        TraceOps.DebugWrite(interpreter, String.Format(
                            "GetInterpreterAutoPathList: Added interpreter library " +
                            "path: {0}", FormatOps.WrapOrNull(libraryPath)), true);
                    }
                }

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

                //
                // NOTE: In "library only" mode, only consider paths which
                //       could contain the core script library.
                //
                if (libraryOnly)
                    return;

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

                StringList autoPathList = interpreter.AutoPathList; /* throw */

                if (autoPathList != null)
                {
                    foreach (string path in autoPathList)
                    {
                        if (!String.IsNullOrEmpty(path) &&
                            !paths.ContainsKey(path) &&
                            (!strictAutoPath || Directory.Exists(path)))
                        {
                            paths.Add(path, null);

                            if (showAutoPath)
                            {
                                TraceOps.DebugWrite(interpreter, String.Format(
                                    "GetInterpreterAutoPathList: Added interpreter " +
                                    "package path: {0}", FormatOps.WrapOrNull(
                                    path)), true);
                            }
                        }
                    }
                }
            }
        }

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

        private static void GetMasterAutoPathList(
            Interpreter interpreter, /* OPTIONAL: May be null. */
            bool libraryOnly,
            bool showAutoPath,
            bool strictAutoPath,
            ref PathDictionary<object> paths
            ) /* THREAD-SAFE */
        {
            lock (syncRoot) /* TRANSACTIONAL */
            {
                if (paths == null)
                    paths = new PathDictionary<object>();

                //
                // TODO: Add any extra "default" package index search paths
                //       here.
                //
                if (!String.IsNullOrEmpty(libraryPath) &&
                    !paths.ContainsKey(libraryPath) &&
                    (!strictAutoPath || Directory.Exists(libraryPath)))
                {
                    paths.Add(libraryPath, null);

                    if (showAutoPath)
                    {
                        TraceOps.DebugWrite(interpreter, String.Format(
                            "GetMasterAutoPathList: Added library path: {0}",
                            FormatOps.WrapOrNull(libraryPath)), true);
                    }
                }

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

                //
                // NOTE: In "library only" mode, only consider paths which
                //       could contain the core script library.
                //
                if (libraryOnly)
                    return;

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

                if (!String.IsNullOrEmpty(tclLibraryPath) &&
                    !paths.ContainsKey(tclLibraryPath) &&
                    (!strictAutoPath || Directory.Exists(tclLibraryPath)))
                {
                    paths.Add(tclLibraryPath, null);

                    if (showAutoPath)
                    {
                        TraceOps.DebugWrite(interpreter, String.Format(
                            "GetMasterAutoPathList: Added Tcl library path: {0}",
                            FormatOps.WrapOrNull(tclLibraryPath)), true);
                    }
                }

                if (autoPathList != null)
                {
                    foreach (string path in autoPathList)
                    {
                        if (!String.IsNullOrEmpty(path) &&
                            !paths.ContainsKey(path) &&
                            (!strictAutoPath || Directory.Exists(path)))
                        {
                            paths.Add(path, null);

                            if (showAutoPath)
                            {
                                TraceOps.DebugWrite(interpreter, String.Format(
                                    "GetMasterAutoPathList: Added package path: {0}",
                                    FormatOps.WrapOrNull(path)), true);
                            }
                        }
                    }
                }

                if (tclAutoPathList != null)
                {
                    foreach (string path in tclAutoPathList)
                    {
                        if (!String.IsNullOrEmpty(path) &&
                            !paths.ContainsKey(path) &&
                            (!strictAutoPath || Directory.Exists(path)))
                        {
                            paths.Add(path, null);

                            if (showAutoPath)
                            {
                                TraceOps.DebugWrite(interpreter, String.Format(
                                    "GetMasterAutoPathList: Added Tcl package path: {0}",
                                    FormatOps.WrapOrNull(path)), true);
                            }
                        }
                    }
                }

#if UNIX
                if (!String.IsNullOrEmpty(unixPackageNameLocalPath) &&
                    !paths.ContainsKey(unixPackageNameLocalPath) &&
                    (!strictAutoPath || Directory.Exists(unixPackageNameLocalPath)))
                {
                    paths.Add(unixPackageNameLocalPath, null);

                    if (showAutoPath)
                    {
                        TraceOps.DebugWrite(interpreter, String.Format(
                            "GetMasterAutoPathList: Added Unix package name local path: {0}",
                            FormatOps.WrapOrNull(unixPackageNameLocalPath)), true);
                    }
                }

                if (!String.IsNullOrEmpty(unixPackageNamePath) &&
                    !paths.ContainsKey(unixPackageNamePath) &&
                    (!strictAutoPath || Directory.Exists(unixPackageNamePath)))
                {
                    paths.Add(unixPackageNamePath, null);

                    if (showAutoPath)
                    {
                        TraceOps.DebugWrite(interpreter, String.Format(
                            "GetMasterAutoPathList: Added Unix package name path: {0}",
                            FormatOps.WrapOrNull(unixPackageNamePath)), true);
                    }
                }
#endif

                if (!String.IsNullOrEmpty(packageNamePath) &&
                    !paths.ContainsKey(packageNamePath) &&
                    (!strictAutoPath || Directory.Exists(packageNamePath)))
                {
                    paths.Add(packageNamePath, null);

                    if (showAutoPath)
                    {
                        TraceOps.DebugWrite(interpreter, String.Format(
                            "GetMasterAutoPathList: Added package name path: {0}",
                            FormatOps.WrapOrNull(packageNamePath)), true);
                    }
                }

                if (!String.IsNullOrEmpty(packageNameRootPath) &&
                    !paths.ContainsKey(packageNameRootPath) &&
                    (!strictAutoPath || Directory.Exists(packageNameRootPath)))
                {
                    paths.Add(packageNameRootPath, null);

                    if (showAutoPath)
                    {
                        TraceOps.DebugWrite(interpreter, String.Format(
                            "GetMasterAutoPathList: Added package name root path: {0}",
                            FormatOps.WrapOrNull(packageNameRootPath)), true);
                    }
                }

                if (!String.IsNullOrEmpty(packagePeerPath) &&
                    !paths.ContainsKey(packagePeerPath) &&
                    (!strictAutoPath || Directory.Exists(packagePeerPath)))
                {
                    paths.Add(packagePeerPath, null);

                    if (showAutoPath)
                    {
                        TraceOps.DebugWrite(interpreter, String.Format(
                            "GetMasterAutoPathList: Added package peer path: {0}",
                            FormatOps.WrapOrNull(packagePeerPath)), true);
                    }
                }

                if (!String.IsNullOrEmpty(packageRootPath) &&
                    !paths.ContainsKey(packageRootPath) &&
                    (!strictAutoPath || Directory.Exists(packageRootPath)))
                {
                    paths.Add(packageRootPath, null);

                    if (showAutoPath)
                    {
                        TraceOps.DebugWrite(interpreter, String.Format(
                            "GetMasterAutoPathList: Added package root path: {0}",
                            FormatOps.WrapOrNull(packageRootPath)), true);
                    }
                }

                if (!String.IsNullOrEmpty(binaryPackageNamePath) &&
                    !paths.ContainsKey(binaryPackageNamePath) &&
                    (!strictAutoPath || Directory.Exists(binaryPackageNamePath)))
                {
                    paths.Add(binaryPackageNamePath, null);

                    if (showAutoPath)
                    {
                        TraceOps.DebugWrite(interpreter, String.Format(
                            "GetMasterAutoPathList: Added binary package name path: {0}",
                            FormatOps.WrapOrNull(binaryPackageNamePath)), true);
                    }
                }

                if (!String.IsNullOrEmpty(binaryPackageRootPath) &&
                    !paths.ContainsKey(binaryPackageRootPath) &&
                    (!strictAutoPath || Directory.Exists(binaryPackageRootPath)))
                {
                    paths.Add(binaryPackageRootPath, null);

                    if (showAutoPath)
                    {
                        TraceOps.DebugWrite(interpreter, String.Format(
                            "GetMasterAutoPathList: Added binary package root path: {0}",
                            FormatOps.WrapOrNull(binaryPackageRootPath)), true);
                    }
                }

                if (!String.IsNullOrEmpty(assemblyPackageNamePath) &&
                    !paths.ContainsKey(assemblyPackageNamePath) &&
                    (!strictAutoPath || Directory.Exists(assemblyPackageNamePath)))
                {
                    paths.Add(assemblyPackageNamePath, null);

                    if (showAutoPath)
                    {
                        TraceOps.DebugWrite(interpreter, String.Format(
                            "GetMasterAutoPathList: Added assembly package name path: {0}",
                            FormatOps.WrapOrNull(assemblyPackageNamePath)), true);
                    }
                }

                if (!String.IsNullOrEmpty(assemblyPackageRootPath) &&
                    !paths.ContainsKey(assemblyPackageRootPath) &&
                    (!strictAutoPath || Directory.Exists(assemblyPackageRootPath)))
                {
                    paths.Add(assemblyPackageRootPath, null);

                    if (showAutoPath)
                    {
                        TraceOps.DebugWrite(interpreter, String.Format(
                            "GetMasterAutoPathList: Added assembly package root path: {0}",
                            FormatOps.WrapOrNull(assemblyPackageRootPath)), true);
                    }
                }
            }
        }

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

        private static void ResetMasterAutoPathList()
        {
            lock (syncRoot)
            {
                if (masterAutoPathList == null)
                    return;

                masterAutoPathList = null;
            }
        }
        #endregion

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

        #region Auto-Path Global Variable Management Methods
        public static void RefreshAutoPathList( /* EXTERNAL USE ONLY */
            bool verbose
            ) /* THREAD-SAFE */
        {
            lock (syncRoot)
            {
                //
                // NOTE: Re-query all of our auto-path related environment
                //       variables now.
                //
                libraryPath = GetEnvironmentVariable(EnvVars.EagleLibrary,
                    false, false, verbose);

                autoPathList = StringList.FromString(GetEnvironmentVariable(
                    EnvVars.EagleLibPath, false, false, verbose));

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

                tclLibraryPath = GetEnvironmentVariable(EnvVars.TclLibrary,
                    true, false, verbose);

                tclAutoPathList = StringList.FromString(GetEnvironmentVariable(
                    EnvVars.TclLibPath, true, false, verbose));

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

                //
                // BUGFIX: Reset the master auto-path so that it will be
                //         initialized again [using our new paths] on the
                //         next call to the GetAutoPathList method.
                //
                ResetMasterAutoPathList();
            }
        }

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

        public static StringList GetAutoPathList( /* CANNOT RETURN NULL */
            Interpreter interpreter, /* OPTIONAL: May be null. */
            bool refresh
            ) /* THREAD-SAFE */
        {
            return GetAutoPathList(interpreter, (interpreter != null) ?
                interpreter.CreateFlags : CreateFlags.None, refresh);
        }

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

        private static StringList GetAutoPathList( /* CANNOT RETURN NULL */
            Interpreter interpreter, /* OPTIONAL: May be null. */
            CreateFlags createFlags,
            bool refresh
            ) /* THREAD-SAFE */
        {
            bool showAutoPath = FlagOps.HasFlags(
                createFlags, CreateFlags.ShowAutoPath, true);

            bool strictAutoPath = FlagOps.HasFlags(
                createFlags, CreateFlags.StrictAutoPath, true);

            if (showAutoPath)
            {
                TraceOps.DebugWrite(interpreter, String.Format(
                    "GetAutoPathList: entered, interpreter = {0}, " +
                    "createFlags = {1}, refresh = {2}",
                    FormatOps.WrapOrNull(interpreter),
                    FormatOps.WrapOrNull(createFlags), refresh), true);
            }

            PathDictionary<object> allPaths = null;

            GetInterpreterAutoPathList(
                interpreter, false, showAutoPath, strictAutoPath,
                ref allPaths);

            lock (syncRoot)
            {
                if (refresh || (masterAutoPathList == null))
                {
                    if (showAutoPath)
                    {
                        TraceOps.DebugWrite(interpreter, String.Format(
                            "GetAutoPathList: Master path list {0}",
                            (masterAutoPathList != null) ? "was initialized" :
                                "was not initialized"), true);
                    }

                    PathDictionary<object> masterPaths = null;

                    GetMasterAutoPathList(
                        interpreter, false, showAutoPath, strictAutoPath,
                        ref masterPaths);

                    masterAutoPathList = (masterPaths != null) ?
                        new StringList(masterPaths.Keys) : new StringList();

                    if (showAutoPath)
                    {
                        TraceOps.DebugWrite(interpreter, String.Format(
                            "GetAutoPathList: Master path list initialized to: {0}",
                            FormatOps.WrapOrNull(masterAutoPathList)), true);
                    }
                }
                else
                {
                    if (showAutoPath)
                    {
                        TraceOps.DebugWrite(interpreter,
                            "GetAutoPathList: Master path list already initialized",
                            true);
                    }
                }

                if (showAutoPath)
                {
                    TraceOps.DebugWrite(interpreter, String.Format(
                        "GetAutoPathList: exited, interpreter = {0}, " +
                        "createFlags = {1}, refresh = {2}",
                        FormatOps.WrapOrNull(interpreter),
                        FormatOps.WrapOrNull(createFlags), refresh), true);
                }

                //
                // NOTE: Merge in master path list into the overall list.
                //
                allPaths.Add(masterAutoPathList, true);
            }

            //
            // NOTE: Create a simple string list beased on the path list and
            //       return it.
            //
            return new StringList(allPaths.Keys);
        }
        #endregion

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

        #region Path Debugging Support Methods
        public static void GetPaths(
            Interpreter interpreter,
            bool all,
            ref StringPairList list
            ) /* THREAD-SAFE */
        {
            if (list == null)
                list = new StringPairList();

            ///////////////////////////////////////////////////////////////////
            //
            // PHASE 0: Interpreter path variables.
            //
            ///////////////////////////////////////////////////////////////////

            if (interpreter != null)
            {
                lock (interpreter.SyncRoot) /* TRANSACTIONAL */
                {
                    string libraryPath = interpreter.LibraryPath; /* throw */

                    list.Add("interpreter library path", libraryPath);

                    StringList autoPathList = interpreter.AutoPathList; /* throw */

                    list.Add("interpreter auto_path list",
                        (autoPathList != null) ? autoPathList.ToString() : null);
                }
            }

            ///////////////////////////////////////////////////////////////////
            //
            // PHASE 1: Basic path variables.
            //
            ///////////////////////////////////////////////////////////////////

            lock (syncRoot) /* TRANSACTIONAL */
            {
                list.Add("binary path", binaryPath);
                list.Add("assembly path", thisAssemblyPath);

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

                list.Add("master base path", masterBasePath);
                list.Add("master library path", masterLibraryPath);

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

                list.Add("library path", libraryPath);
                list.Add("Tcl library path", tclLibraryPath);

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

                list.Add("package name path", packageNamePath);
                list.Add("package name root path", packageNameRootPath);

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

#if NATIVE && TCL
                list.Add("Tcl package name path", tclPackageNamePath);
                list.Add("Tcl package name root path", tclPackageNameRootPath);
#endif

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

#if UNIX
                list.Add("Unix package name local path",
                    unixPackageNameLocalPath);

                list.Add("Unix package name path", unixPackageNamePath);
#endif

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

                list.Add("package peer path", packagePeerPath);
                list.Add("package root path", packageRootPath);

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

                list.Add("binary package name path", binaryPackageNamePath);
                list.Add("binary package root path", binaryPackageRootPath);

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

                list.Add("assembly package name path", assemblyPackageNamePath);
                list.Add("assembly package root path", assemblyPackageRootPath);

                ///////////////////////////////////////////////////////////////
                //
                // PHASE 2: Path lists.
                //
                ///////////////////////////////////////////////////////////////

                list.Add("auto_path list", (autoPathList != null) ?
                    autoPathList.ToString() : null);

                list.Add("Tcl auto_path list", (tclAutoPathList != null) ?
                    tclAutoPathList.ToString() : null);

                list.Add("master auto_path list", (masterAutoPathList != null) ?
                    masterAutoPathList.ToString() : null);

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

                if (!all)
                    return;

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

                string prefix;

                ///////////////////////////////////////////////////////////////
                //
                // PHASE 3: Path resolution routines.
                //
                ///////////////////////////////////////////////////////////////

                prefix = "Path.GetDirectoryName(GetBinaryPath())";

                try
                {
                    list.Add(prefix, Path.GetDirectoryName(GetBinaryPath()));
                }
                catch (Exception e)
                {
                    list.Add(prefix, e.ToString());
                }

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

                prefix = "Path.GetFullPath(Path.GetDirectoryName(GetBinaryPath()))";

                try
                {
                    string directory = Path.GetDirectoryName(GetBinaryPath());

                    list.Add(prefix, (directory != null) ?
                        Path.GetFullPath(directory) : null);
                }
                catch (Exception e)
                {
                    list.Add(prefix, e.ToString());
                }

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

                prefix = "GetBasePath()";

                try
                {
                    list.Add(prefix, GetBasePath());
                }
                catch (Exception e)
                {
                    list.Add(prefix, e.ToString());
                }

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

                prefix = "Path.GetFullPath(GetBasePath())";

                try
                {
                    string basePath = GetBasePath();

                    list.Add(prefix, (basePath != null) ?
                        Path.GetFullPath(basePath) : null);
                }
                catch (Exception e)
                {
                    list.Add(prefix, e.ToString());
                }

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

                prefix = "GetRawBinaryBasePath()";

                try
                {
                    list.Add(prefix, GetRawBinaryBasePath());
                }
                catch (Exception e)
                {
                    list.Add(prefix, e.ToString());
                }

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

                prefix = "Path.GetFullPath(GetRawBinaryBasePath())";

                try
                {
                    string basePath = GetRawBinaryBasePath();

                    list.Add(prefix, (basePath != null) ?
                        Path.GetFullPath(basePath) : null);
                }
                catch (Exception e)
                {
                    list.Add(prefix, e.ToString());
                }

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

                prefix = "GetRawBasePath(GetAssemblyPath())";

                try
                {
                    list.Add(prefix, GetRawBasePath(GetAssemblyPath()));
                }
                catch (Exception e)
                {
                    list.Add(prefix, e.ToString());
                }

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

                prefix = "Path.GetFullPath(GetRawBasePath(GetAssemblyPath()))";

                try
                {
                    string basePath = GetRawBasePath(GetAssemblyPath());

                    list.Add(prefix, (basePath != null) ?
                        Path.GetFullPath(basePath) : null);
                }
                catch (Exception e)
                {
                    list.Add(prefix, e.ToString());
                }
            }
        }

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

        public static void DisplayPaths(
            Interpreter interpreter,
            bool all
            ) /* THREAD-SAFE */
        {
            StringPairList list = null;

            GetPaths(interpreter, all, ref list);

            if (list != null)
            {
                foreach (IPair<string> element in list)
                {
                    if (element != null)
                    {
                        /* EXEMPT */
                        DebugOps.Write(
                            interpreter, String.Format("{0} = {1}",
                            element.X, FormatOps.DisplayValue(
                            element.Y)), true);
                    }
                }
            }
        }
        #endregion

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

        #region Embedding Support Methods
        public static bool DetectLibraryPath( /* EXTERNAL USE ONLY */
            Assembly assembly
            ) /* THREAD-SAFE */
        {
            lock (syncRoot) /* TRANSACTIONAL */
            {
                string name = GetPackageName();

                if (!String.IsNullOrEmpty(name))
                {
                    Version version = GetPackageVersion();

                    if (version != null)
                    {
                        string path = GetPackagePath(
                            assembly, name, version, true);

                        string fileName = FileName.Initialization;

                        if (String.IsNullOrEmpty(path) || !Directory.Exists(path) ||
                            !File.Exists(PathOps.CombinePath(null, path, fileName)))
                        {
                            path = GetEnvironmentVariable(EnvVars.Eagle);

                            if (!String.IsNullOrEmpty(path) &&
                                Directory.Exists(path))
                            {
                                path = PathOps.CombinePath(null, path,
                                    FormatOps.PackageDirectory(name, version, true));

                                if (!Directory.Exists(path) ||
                                    !File.Exists(PathOps.CombinePath(null, path, fileName)))
                                {
                                    path = null;
                                }
                            }
                            else
                            {
                                path = null;
                            }
                        }
                        else
                        {
                            path = null;
                        }

                        //
                        // NOTE: Did we manage to find a suitable library path?
                        //
                        if (!String.IsNullOrEmpty(path))
                        {
                            SetLibraryPath(path, true);

                            return true;
                        }
                    }
                }
            }

            return false;
        }
        #endregion
    }
}
