/*
 * NativePackage.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 Eagle._Attributes;
using Eagle._Components.Private;
using Eagle._Components.Private.Tcl;
using Eagle._Constants;
using Eagle._Containers.Private;
using Eagle._Containers.Public;
using Eagle._Interfaces.Private.Tcl;
using Eagle._Interfaces.Public;

namespace Eagle._Components.Public
{
    [ObjectId("2e8eae65-3e12-4eb9-8695-c871cc23a57a")]
    public static class NativePackage
    {
        #region Private Constants
        //
        // NOTE: This is the name of the native command in the Tcl interpreter
        //       that will be bridged to the Eagle interpreter (i.e. the native
        //       endpoint).
        //
        private static readonly string nativeCommandName =
            GlobalState.GetPackageNameNoCase();

        //
        // NOTE: This is the name of the managed command in the Eagle
        //       interpreter that will be bridged to the Tcl interpreter
        //       (i.e. the managed endpoint).
        //
        private static readonly string managedCommandName =
            typeof(_Commands.Eval).Name.ToLowerInvariant();

        //
        // NOTE: These name prefixes are used when building the script-visible
        //       names for the Tcl interpreters used by this class.  Also, see
        //       the constants tclMasterInterpPrefix, tclSafeInterpPrefix, and
        //       tclInterpPrefix in the Interpreter class.
        //
        private const string tclNativeMasterInterpPrefix = "nativeMasterInterp";
        private const string tclNativeSafeMasterInterpPrefix = "nativeSafeMasterInterp";
        private const string tclNativeInterpPrefix = "nativeInterp";
        private const string tclNativeSafeInterpPrefix = "nativeSafeInterp";

        //
        // NOTE: In the argument string passed from native code, we need at
        //       least the module handle, the Tcl interpreter pointer, and the
        //       Tcl interpreter safety indicator.
        //
        private const int MinimumArgumentCountV1 = 4;
        private const int MinimumArgumentCountV2 = MinimumArgumentCountV1 + 2;

        //
        // NOTE: The possible protocol identifier strings that we should expect
        //       in the argument string passed from native code.
        //
        private const string ProtocolIdV1 = "Garuda_v1.0";
        private const string ProtocolIdV2 = "Garuda_v1.0_r2.0";

        //
        // NOTE: These are the error messages returned when the string argument
        //       cannot be parsed properly according to the selected protocol
        //       version (i.e. not a list, not enough sub-arguments, etc).
        //
        private const string ParseArgumentErrorV1 = "could not parse " +
            "argument string, expected at least [{0} <IntPtr> <IntPtr> " +
            "<Boolean>]: {1}";

        private const string ParseArgumentErrorV2 = "could not parse " +
            "argument string, expected at least [{0} <IntPtr> <IntPtr> " +
            "<IntPtr> <Boolean> <Boolean>]: {1}";

        //
        // NOTE: This is the error message returned when the "safe" mode of the
        //       Eagle interpreter is unsuitable for the "safe" mode of the Tcl
        //       interpreter.
        //
        private const string SafeUnsafeError = "cannot use safe Tcl " +
            "interpreter {0} with unsafe Eagle interpreter {1}";

        //
        // NOTE: This is the error message returned when the Tcl interpreter
        //       has already been attached to the bridge.
        //
        private const string AlreadyAttachedError = "cannot attach Tcl " +
            "interpreter {0} to Eagle interpreter {1}, already attached";

        //
        // NOTE: This is the error message returned when the Tcl interpreter
        //       cannot be detached from the bridge.
        //
        private const string CouldNotDetachError = "could not detach Tcl " +
            "interpreter {0} from Eagle interpreter {1}";
        #endregion

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

        #region Private Data
        //
        // NOTE: This object is used with the lock statement to protect
        //       access to the other static fields.
        //
        private static readonly object syncRoot = new object();

        //
        // NOTE: The Eagle interpreters holding the Tcl integration components
        //       created and used by this class.
        //
        private static IntPtrInterpreterDictionary interpreters;

        //
        // NOTE: The list of Tcl interpreters we know originated outside the
        //       direct control of Eagle (i.e. we should not try to delete
        //       them).
        //
        private static IntPtrDictionary tclInterps = null;
        #endregion

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

        #region Non-Public Methods
        #region Private Tcl Interpreter Management Methods
        private static string GetTclInterpreterPrefix(
            bool master,
            bool safe
            )
        {
            if (master)
                return safe ? tclNativeSafeMasterInterpPrefix :
                    tclNativeMasterInterpPrefix;
            else
                return safe ? tclNativeSafeInterpPrefix :
                    tclNativeInterpPrefix;
        }

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

        private static bool HasTclInterpreters(
            ref Result error
            )
        {
            lock (syncRoot)
            {
                bool result = (tclInterps != null);

                if (!result)
                    error = "no Tcl interpreters available";

                return result;
            }
        }
        #endregion

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

        #region Internal Tcl Interpreter Management Methods
        //
        // NOTE: Used by the CheckInterp method of the TclApi class.
        //
        internal static bool FindTclInterpreterThreadId(
            IntPtr interp,
            ref int threadId
            )
        {
            lock (syncRoot)
            {
                if ((tclInterps != null) &&
                    tclInterps.ContainsValue(interp) /* O(N) */)
                {
                    //
                    // NOTE: GetInterpreter() call OK, local value is
                    //       not used elsewhere.
                    //
                    Interpreter interpreter = GetInterpreter(interp);

                    if (interpreter == null)
                        interpreter = GetPrimaryInterpreter();

                    if (interpreter != null)
                    {
                        int localThreadId = interpreter.ThreadId;

                        if (localThreadId != 0)
                        {
                            threadId = localThreadId;
                            return true;
                        }
                    }
                }

                return false;
            }
        }

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

        //
        // NOTE: Used by the DoOneEvent method of the TclWrapper class.
        //
        internal static bool IsTclInterpreterActive()
        {
            lock (syncRoot)
            {
                return ((interpreters != null) && (interpreters.Count > 0) &&
                    (tclInterps != null) && (tclInterps.Count > 0));
            }
        }

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

        //
        // NOTE: Used indirectly by the Execute method of the _Commands.Tcl
        //       class to help implement the [tcl master] sub-command.
        //
        internal static ReturnCode GetMasterTclInterpreter(
            Interpreter interpreter,
            ref string name,
            ref IntPtr interp,
            ref Result error
            )
        {
            lock (syncRoot)
            {
                if (HasTclInterpreters(ref error))
                {
                    //
                    // HACK: Use the convention that we always prefix the
                    //       master Tcl interpreter with a specially formatted
                    //       name in order to find it (we cannot simply use the
                    //       Tcl interpreter at index zero because the Tcl
                    //       interpreters are maintained in a dictionary).
                    //
                    string pattern = FormatOps.Id(
                        tclNativeMasterInterpPrefix, null,
                        Characters.Asterisk.ToString());

                    string safePattern = FormatOps.Id(
                        tclNativeSafeMasterInterpPrefix, null,
                        Characters.Asterisk.ToString());

                    //
                    // NOTE: Search all the Tcl interpreters in this interpreter
                    //       for the master Tcl interp stopping as soon as we
                    //       find a valid one (there should only be one valid
                    //       master Tcl interpreter per interpreter at any given
                    //       time).
                    //
                    string key = null;
                    IntPtr value = IntPtr.Zero;

                    foreach (KeyValuePair<string, IntPtr> pair in tclInterps)
                    {
                        //
                        // NOTE: First, make sure the native Tcl interpreter
                        //       belongs to the specified Eagle interpreter,
                        //       if any.
                        //
                        // NOTE: GetInterpreter() call OK, local value is
                        //       not used elsewhere.
                        //
                        // NOTE: GetPrimaryInterpreter() call OK, local
                        //       value is not used elsewhere.
                        //
                        if ((interpreter != null) &&
                            !Object.ReferenceEquals(
                                GetInterpreter(pair.Value), interpreter) &&
                            !Object.ReferenceEquals(
                                GetPrimaryInterpreter(), interpreter))
                        {
                            continue;
                        }

                        //
                        // NOTE: Second, make sure it is a master (or "safe"
                        //       master) Tcl interpreter.  Hard-coded match
                        //       mode is OK here.
                        //
                        if ((StringOps.Match(
                                interpreter, MatchMode.Glob, pair.Key, pattern, false) ||
                            StringOps.Match(
                                interpreter, MatchMode.Glob, pair.Key, safePattern, false)) &&
                            (pair.Value != IntPtr.Zero))
                        {
                            key = pair.Key;
                            value = pair.Value;

                            break;
                        }
                    }

                    //
                    // NOTE: Did we find the handle for the master Tcl
                    //       interpreter?
                    //
                    if (key != null)
                    {
                        name = key;
                        interp = value;

                        return ReturnCode.Ok;
                    }
                    else
                    {
                        error = "Tcl master interpreter is not available";
                    }
                }
            }

            return ReturnCode.Error;
        }

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

        //
        // NOTE: Used by the DeleteTclInterpreter and DisposeTcl methods of
        //       the Interpreter class.
        //
        internal static bool ShouldDeleteTclInterpreter(
            string interpName,
            IntPtr interp
            )
        {
            //
            // NOTE: Return true if we are not aware of the Tcl interpreter;
            //       otherwise, we must return false because it is not
            //       actually owned by Eagle and we should not delete it.
            //
            lock (syncRoot)
            {
                return (tclInterps == null) ||
                    !tclInterps.ContainsValue(interp); /* O(N) */
            }
        }
        #endregion

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

        #region Private Eagle Interpreter Management Methods
        //
        // WARNING: Normally, this method should not be called directly.
        //          All exceptions to this rule should be marked in the
        //          neighboring source code comments.
        //
        private static Interpreter GetInterpreter(
            IntPtr interp
            )
        {
            lock (syncRoot)
            {
                Interpreter interpreter = null;

                if ((interpreters != null) &&
                    interpreters.TryGetValue(interp, out interpreter))
                {
                    return interpreter;
                }

                TraceOps.DebugTrace(String.Format(
                    "{0}: GetInterpreter: failed, interp = {1}, " +
                    "interpreter = {2}, {3}{4}",
                    GlobalState.GetCurrentThreadId(), interp,
                    FormatOps.WrapOrNull(interpreter),
                    (interpreters != null) ? "not found" : "unavailable",
                    Environment.NewLine), typeof(NativePackage).Name);

                return null;
            }
        }

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

        //
        // WARNING: For use by GetPrimaryOrIsolatedInterpreter() only.
        //          All exceptions to this rule *MUST* be marked in the
        //          neighboring source code comments.
        //
        private static Interpreter GetPrimaryInterpreter()
        {
            //
            // NOTE: GetInterpreter() call OK, the GetPrimaryInterpreter()
            //       method is trusted implicitly and should be used with
            //       great care.
            //
            return GetInterpreter(IntPtr.Zero);
        }

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

        private static Interpreter GetPrimaryOrIsolatedInterpreter(
            IntPtr interp,
            bool isolated
            )
        {
            //
            // NOTE: GetInterpreter() call OK, the purpose of this method
            //       is to return it, if necessary.
            //
            // NOTE: GetPrimaryInterpreter() call OK, the purpose of this
            //       method is to return it, if necessary.
            //
            return isolated ?
                GetInterpreter(interp) : GetPrimaryInterpreter();
        }

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

        private static bool AddInterpreter(
            IntPtr interp,
            Interpreter interpreter
            )
        {
            lock (syncRoot)
            {
                if (interpreters == null)
                    interpreters = new IntPtrInterpreterDictionary();

                if (interpreters.ContainsKey(interp))
                {
                    TraceOps.DebugTrace(String.Format(
                        "{0}: AddInterpreter: failed, interp = {1}, " +
                        "interpreter = {2}, already exists{3}",
                        GlobalState.GetCurrentThreadId(), interp,
                        FormatOps.WrapOrNull(interpreter),
                        Environment.NewLine), typeof(NativePackage).Name);

                    return false;
                }

                interpreters.Add(interp, interpreter);
                return true;
            }
        }

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

        private static bool RemoveInterpreter(
            IntPtr interp,
            Interpreter interpreter
            )
        {
            lock (syncRoot)
            {
                if (interpreters == null)
                    interpreters = new IntPtrInterpreterDictionary();

#if DEBUG
                Interpreter localInterpreter;

                if (!interpreters.TryGetValue(interp, out localInterpreter))
                {
                    TraceOps.DebugTrace(String.Format(
                        "{0}: RemoveInterpreter: failed, interp = {1}, " +
                        "interpreter = {2}, does not exist{3}",
                        GlobalState.GetCurrentThreadId(), interp,
                        FormatOps.WrapOrNull(interpreter),
                        Environment.NewLine), typeof(NativePackage).Name);

                    return false;
                }

                if (!Object.ReferenceEquals(localInterpreter, interpreter))
                {
                    TraceOps.DebugTrace(String.Format(
                        "{0}: RemoveInterpreter: failed, interp = {1}, " +
                        "interpreter = {2}, localInterpreter = {3}, " +
                        "mismatched{3}", GlobalState.GetCurrentThreadId(),
                        interp, FormatOps.WrapOrNull(interpreter),
                        FormatOps.WrapOrNull(localInterpreter),
                        Environment.NewLine), typeof(NativePackage).Name);

                    return false;
                }
#endif

                return interpreters.Remove(interp);
            }
        }

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

        private static bool DisposeInterpreter(
            IntPtr interp,
            Interpreter interpreter
            )
        {
            bool result = false;

            TraceOps.DebugTrace(String.Format(
                "{0}: DisposeInterpreter: entered, interp = {1}, " +
                "interpreter = {2}{3}", GlobalState.GetCurrentThreadId(),
                interp, FormatOps.WrapOrNull(interpreter),
                Environment.NewLine), typeof(NativePackage).Name);

            if (interpreter != null)
            {
                //
                // NOTE: Attempt to dispose of the Eagle interpreter
                //       now.  In theory, this may throw exceptions;
                //       however, since it should not attempt to
                //       unload Tcl, so that is unlikely.
                //
                interpreter.Dispose(); /* throw */

                //
                // NOTE: Next, make sure the interpreter is removed
                //       from the static dictionary for this class.
                //
                result = RemoveInterpreter(interp, interpreter);

                //
                // NOTE: Finally, null it out for good measure.
                //
                interpreter = null;
            }

            TraceOps.DebugTrace(String.Format(
                "{0}: DisposeInterpreter: exited, interp = {1}, " +
                "interpreter = {2}, result = {3}{4}",
                GlobalState.GetCurrentThreadId(),
                interp, FormatOps.WrapOrNull(interpreter), result,
                Environment.NewLine), typeof(NativePackage).Name);

            return result;
        }

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

        private static int DisposeInterpreters(
            IntPtr interp
            )
        {
            lock (syncRoot)
            {
                TraceOps.DebugTrace(String.Format(
                    "{0}: DisposeInterpreters: entered, interp = {1}{2}",
                    GlobalState.GetCurrentThreadId(), interp,
                    Environment.NewLine), typeof(NativePackage).Name);

                int[] count = { 0, 0 };

                if (interpreters != null)
                {
                    IntPtrInterpreterDictionary localInterpreters =
                        interpreters.Clone() as IntPtrInterpreterDictionary;

                    if (localInterpreters != null)
                    {
                        count[1] = localInterpreters.Count;

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

                            try
                            {
                                if (DisposeInterpreter(pair.Key, interpreter))
                                    count[0]++;
                            }
                            catch (Exception e)
                            {
                                TraceOps.DebugTrace(String.Format(
                                    "{0}: {1}{2}",
                                    GlobalState.GetCurrentThreadId(),
                                    e, Environment.NewLine),
                                    typeof(NativePackage).Name);
                            }
                        }

                        localInterpreters.Clear();
                        localInterpreters = null;
                    }
                }

                TraceOps.DebugTrace(String.Format(
                    "{0}: DisposeInterpreters: exited, interp = {1}, " +
                    "disposed = {2}/{3}{4}", GlobalState.GetCurrentThreadId(),
                    interp, count[0], count[1], Environment.NewLine),
                    typeof(NativePackage).Name);

                return count[0];
            }
        }
        #endregion

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

        #region Internal State Introspection Methods
        //
        // NOTE: Used by the _Hosts.Default.BuildEngineInfoList method.
        //
        internal static void AddInfo(
            StringPairList list,
            bool empty
            )
        {
            if (list == null)
                return;

            lock (syncRoot)
            {
                StringPairList localList = new StringPairList();

                if (empty || ((interpreters != null) && (interpreters.Count > 0)))
                    localList.Add("Interpreters", (interpreters != null) ?
                        interpreters.Count.ToString() : FormatOps.DisplayNull);

                //
                // NOTE: GetPrimaryInterpreter() call OK, local value is not
                //       used elsewhere.
                //
                Interpreter interpreter = GetPrimaryInterpreter();

                if (empty || (interpreter != null))
                    localList.Add("PrimaryInterpreter", (interpreter != null) ?
                        interpreter.ToString() : FormatOps.DisplayNull);

                if (empty || ((tclInterps != null) && (tclInterps.Count > 0)))
                    localList.Add("TclInterps", (tclInterps != null) ?
                        tclInterps.Count.ToString() : FormatOps.DisplayNull);

                if (localList.Count > 0)
                {
                    list.Add((IPair<string>)null);
                    list.Add("Native Package");
                    list.Add((IPair<string>)null);
                    list.Add(localList);
                }
            }
        }
        #endregion

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

        #region Private Helper Methods
        private static void Complain(
            IntPtr interp,
            bool isolated,
            ReturnCode code,
            Result result
            )
        {
            DebugOps.Complain(
                GetPrimaryOrIsolatedInterpreter(interp, isolated),
                code, result);
        }
        #endregion
        #endregion

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

        #region Private Argument Handling Methods
        private static ReturnCode CheckProtocolId(
            string protocolId,
            ref bool haveExtra,
            ref Result error
            )
        {
            if (String.Equals(protocolId, ProtocolIdV1,
                    StringOps.SystemStringComparisonType))
            {
                haveExtra = false;
                return ReturnCode.Ok;
            }

            if (String.Equals(protocolId, ProtocolIdV2,
                    StringOps.SystemStringComparisonType))
            {
                haveExtra = true;
                return ReturnCode.Ok;
            }

            error = String.Format(
                "protocol mismatch, have \"{0}\", need \"{1}\" or \"{2}\"",
                protocolId, ProtocolIdV1, ProtocolIdV2);

            return ReturnCode.Error;
        }

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

        private static IntPtr StringToIntPtr(
            string text,
            ref Result error
            )
        {
            long value = 0;

            if (Value.GetWideInteger2(
                    text, ValueFlags.AnyInteger, null, ref value,
                    ref error) == ReturnCode.Ok)
            {
                return new IntPtr(value);
            }

            return IntPtr.Zero;
        }

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

        private static ReturnCode ParseArgument(
            string arg,
            ref string protocolId,
            ref IntPtr module,
            ref IntPtr stubs,
            ref IntPtr interp,
            ref bool isolated,
            ref bool safe,
            ref StringList list,
            ref Result error
            )
        {
            StringList localList = null;
            Result localError = null;
            bool haveExtra = false;

            //
            // NOTE: Attempt to parse the argument as a Tcl/Eagle list.  If
            //       this fails, we cannot continue.
            //
            if (Parser.SplitList(
                    null, arg, 0, Length.Invalid, false, ref localList,
                    ref localError) != ReturnCode.Ok)
            {
                goto done;
            }

            //
            // NOTE: Make sure the parsed list has at least the minimum number
            //       of arguments we need (i.e. the Tcl library module handle,
            //       the Tcl interpreter pointer, etc).
            //
            if (localList.Count < MinimumArgumentCountV1)
            {
                localError = String.Format(
                    "not enough arguments for \"{0}\", have {1}, need {2}",
                    ProtocolIdV1, localList.Count, MinimumArgumentCountV1);

                goto done;
            }

            //
            // NOTE: Initially, start at the first element of the parsed list,
            //       which is always the protocol identifier.
            //
            int index = 0;

            //
            // NOTE: The first argument must be the protocol identifier and it
            //       must represent a supported protocol version.  This check
            //       will also tell us if we should expect an additional
            //       argument containing the Tcl C API stubs structure pointer.
            //
            string localProtocolId = localList[index];

            if (CheckProtocolId(localProtocolId,
                    ref haveExtra, ref localError) != ReturnCode.Ok)
            {
                goto done;
            }

            //
            // NOTE: Make sure the parsed list has at least the minimum number
            //       of arguments we need, taking into account the Tcl C API
            //       stubs structure pointer argument, if applicable.
            //
            if (haveExtra && (localList.Count < MinimumArgumentCountV2))
            {
                localError = String.Format(
                    "not enough arguments for \"{0}\", have {1}, need {2}",
                    ProtocolIdV2, localList.Count, MinimumArgumentCountV2);

                goto done;
            }

            //
            // NOTE: Advance to the next element, which is always the Tcl
            //       library module handle.
            //
            index++;

            //
            // NOTE: Assume the second argument of the list parsed from the
            //       argument actually represents the Tcl library module
            //       handle.
            //
            IntPtr localModule = StringToIntPtr(localList[index],
                ref localError);

            if (localModule == IntPtr.Zero)
                goto done;

            //
            // NOTE: Advance to the next element, which is either the pointer to
            //       the Tcl C API stubs structure -OR- the pointer to the Tcl
            //       interpreter.
            //
            index++;

            IntPtr localStubs = IntPtr.Zero;

            if (haveExtra)
            {
                //
                // NOTE: Assume the third argument of the list parsed from the
                //       argument actually represents the pointer to the Tcl C
                //       API stubs structure.
                //
                localStubs = StringToIntPtr(localList[index], ref localError);

                if (localStubs == IntPtr.Zero)
                    goto done;

                //
                // NOTE: Advance to the next element, which is the pointer to
                //       the Tcl interpreter.
                //
                index++;
            }

            //
            // NOTE: Assume the third (or fourth) argument of the list parsed
            //       from the argument actually represents the pointer to the
            //       Tcl interpreter.
            //
            IntPtr localInterp = StringToIntPtr(localList[index],
                ref localError);

            if (localInterp == IntPtr.Zero)
                goto done;

            //
            // NOTE: Advance to the next element, which is either the Tcl
            //       interpreter safety indicator -OR- the Eagle isolated
            //       interpreter indicator.
            //
            index++;

            bool localIsolated = false;

            if (haveExtra)
            {
                //
                // NOTE: The fourth (or fifth) argument is the Tcl interpreter
                //       safety indicator.  Non-zero means the Tcl interpreter
                //       is "safe" and the Eagle interpreter MUST be as well.
                //
                if (Value.GetBoolean2(
                        localList[index], ValueFlags.AnyBoolean, null,
                        ref localIsolated, ref localError) != ReturnCode.Ok)
                {
                    goto done;
                }

                //
                // NOTE: Advance to the next element, which is always the Tcl
                //       interpreter safety indicator.
                //
                index++;
            }

            //
            // NOTE: The fifth (or sixth) argument is the Tcl interpreter
            //       safety indicator.  Non-zero means the Tcl interpreter is
            //       "safe" and the Eagle interpreter MUST be as well.
            //
            bool localSafe = false;

            if (Value.GetBoolean2(localList[index], ValueFlags.AnyBoolean,
                    null, ref localSafe, ref localError) != ReturnCode.Ok)
            {
                goto done;
            }

            //
            // NOTE: Advance to the next element, which is always the start
            //       of the remaining (optional) arguments, if any.
            //
            index++;

            //
            // NOTE: Ok, everything was successful.  Commit changes to the
            //       parameters provided by the caller starting with the
            //       argument list just in case the called method throws an
            //       exception.
            //
            list = StringList.GetRange(localList, index, true);

            protocolId = localProtocolId;
            interp = localInterp;
            module = localModule;
            stubs = localStubs;
            isolated = localIsolated;
            safe = localSafe;

            return ReturnCode.Ok;

        done:
            error = haveExtra ?
                String.Format(ParseArgumentErrorV2, ProtocolIdV2, localError) :
                String.Format(ParseArgumentErrorV1, ProtocolIdV1, localError);

            return ReturnCode.Error;
        }
        #endregion

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

        #region Public CLR Native API Integration Methods
        //
        // WARNING: This method is used to integrate with native code via the
        //          native CLR API.
        //
        public static int Startup(
            string argument /* This is the value of the "pwzArgument" argument
                             * as it was passed to native CLR API method
                             * ICLRRuntimeHost.ExecuteInDefaultAppDomain. */
            ) /* ENTRY-POINT, THREAD-SAFE */
        {
            ReturnCode code;
            string protocolId = null;
            IntPtr module = IntPtr.Zero;
            IntPtr stubs = IntPtr.Zero;
            IntPtr interp = IntPtr.Zero;
            bool isolated = false;
            bool safe = false;
            StringList list = null;
            Result result = null;

            TraceOps.DebugTrace(String.Format(
                "{0}: Startup: entered, argument = {1}{2}",
                GlobalState.GetCurrentThreadId(),
                FormatOps.WrapOrNull(true, true, argument),
                Environment.NewLine), typeof(NativePackage).Name);

            code = ParseArgument(
                argument, ref protocolId, ref module, ref stubs, ref interp,
                ref isolated, ref safe, ref list, ref result);

            if (code == ReturnCode.Ok)
            {
                bool[] created = new bool[] { false, false, false };
                Interpreter interpreter = null;
                ITclApi tclApi = null;
                TclBridge tclBridge = null;

                try
                {
                    //
                    // NOTE: Lock access to the static data contained in this
                    //       class (e.g. the Eagle interpreter we use to host
                    //       the Tcl integration components).
                    //
                    lock (syncRoot)
                    {
                        //
                        // NOTE: We will need an Eagle interpreter to host the
                        //       necessary Tcl integration components;
                        //       therefore, attempt to create one now.  This
                        //       Eagle interpreter MUST be kept around for the
                        //       entire lifetime of the Tcl interpreter we are
                        //       integrating with and MUST be visible to the
                        //       CLR GC; therefore, we store it in a static
                        //       field of this class.  This is not an ideal
                        //       solution because it prevents us from having
                        //       multiple Tcl interpreters with a one-to-one
                        //       relationship to Eagle interpreters.
                        //
                        interpreter = GetPrimaryOrIsolatedInterpreter(
                            interp, isolated);

                        if (interpreter == null)
                        {
                            TraceOps.DebugTrace(String.Format(
                                "{0}: Startup: appDomain = {1}{2}",
                                GlobalState.GetCurrentThreadId(),
                                FormatOps.DisplayAppDomain(),
                                Environment.NewLine),
                                typeof(NativePackage).Name);

                            //
                            // NOTE: If there are any Tcl interpreters in our
                            //       list at this point, they are stale and
                            //       should be removed.
                            //
                            if (tclInterps != null)
                                tclInterps.Clear();

                            CreateFlags createFlags =
                                Interpreter.GetStartupCreateFlags(
                                    list, CreateFlags.NativeUse,
                                    OptionOriginFlags.All, true, true);

                            if (safe)
                                createFlags |= CreateFlags.SafeAndHideUnsafe;

                            string libraryPath = null;

                            code = Interpreter.GetStartupLibraryPath(
                                list, createFlags, OptionOriginFlags.All, true,
                                true, ref libraryPath, ref result);

                            if (code == ReturnCode.Ok)
                            {
                                interpreter = Interpreter.Create(
                                    list, createFlags, libraryPath, ref result);

                                created[0] = true; /* NOTE: We own it. */

                                if (interpreter != null)
                                {
                                    code = Interpreter.ProcessStartupOptions(
                                        interpreter, list, createFlags,
                                        OptionOriginFlags.All, true, true,
                                        ref result);
                                }
                            }

                            TraceOps.DebugTrace(String.Format(
                                "{0}: Startup: interpreter {1}, args = {2}, " +
                                "createFlags = {3}, libraryPath = {4}, " +
                                "code = {5}, result = {6}{7}",
                                GlobalState.GetCurrentThreadId(),
                                (interpreter != null) ? "created" : "not created",
                                FormatOps.WrapOrNull(true, true, list),
                                FormatOps.WrapOrNull(createFlags),
                                FormatOps.WrapOrNull(libraryPath), code,
                                FormatOps.WrapOrNull(true, true, result),
                                Environment.NewLine), typeof(NativePackage).Name);
                        }

                        //
                        // NOTE: Did we succeed at creating the Eagle
                        //       interpreter [at some point]?
                        //
                        if ((code == ReturnCode.Ok) && (interpreter != null))
                        {
                            //
                            // NOTE: Verify that the Eagle interpreter is safe
                            //       if the Tcl interpreter is safe (i.e. just
                            //       in case the Eagle interpreter was created
                            //       previously with an unsafe Tcl interpreter).
                            //
                            if (!safe || interpreter.IsSafe())
                            {
                                //
                                // NOTE: Lookup the Eagle [eval] command by
                                //       name and grab the IExecute object for
                                //       it.  This command will be the
                                //       destination for the [eagle] command in
                                //       Tcl added by this package.
                                //
                                IExecute execute = null;

                                code = interpreter.GetIExecuteViaResolvers(
                                    interpreter.GetResolveEngineFlags(true),
                                    managedCommandName,
                                    new ArgumentList(managedCommandName),
                                    LookupFlags.Default, ref execute,
                                    ref result);

                                if (code == ReturnCode.Ok)
                                {
                                    //
                                    // NOTE: Grab the lock for the Tcl related
                                    //       data of the interpreter.
                                    //
                                    lock (interpreter.TclSyncRoot)
                                    {
                                        //
                                        // NOTE: Create the TclApi object based on
                                        //       the provided Tcl library module
                                        //       handle.  If the supplied module
                                        //       handle is invaild in some way,
                                        //       this is where the failure will
                                        //       likely be [noticed first].
                                        //
                                        tclApi = TclApi.GetTclApi(interpreter);

                                        if (tclApi == null)
                                        {
                                            tclApi = TclApi.Create(
                                                interpreter, null, null, module,
                                                stubs, ref result);

                                            created[1] = true; /* NOTE: We own it. */

                                            TraceOps.DebugTrace(String.Format(
                                                "{0}: Startup: tclApi {1}, " +
                                                "module = {2}, stubs = {3}, " +
                                                "code = {4}, result = {5}{6}",
                                                GlobalState.GetCurrentThreadId(),
                                                (tclApi != null) ? "created" :
                                                    "not created", module, stubs,
                                                code, FormatOps.WrapOrNull(
                                                    true, true, result),
                                                Environment.NewLine),
                                                typeof(NativePackage).Name);
                                        }

                                        if (tclApi != null)
                                        {
                                            //
                                            // NOTE: Make sure the list of Tcl
                                            //       interpreters [that we should
                                            //       not delete] is initialized.
                                            //
                                            if (tclInterps == null)
                                                tclInterps = new IntPtrDictionary();

                                            //
                                            // NOTE: Make sure this Tcl interpreter
                                            //       is not already attached.
                                            //
                                            if (!tclInterps.ContainsValue(interp)) /* O(N) */
                                            {
                                                //
                                                // NOTE: All isolated Tcl interpreters
                                                //       are a "master" Tcl interpreter.
                                                //       However, only the first shared
                                                //       one is.
                                                //
                                                string interpName = FormatOps.Id(
                                                    GetTclInterpreterPrefix(
                                                        isolated || (tclInterps.Count == 0),
                                                        safe), null, interpreter.NextId());

                                                tclInterps.Add(interpName, interp);

                                                TclApi.AddInterp(interpreter,
                                                    interpName, interp);

                                                TclApi.SetTclApi(interpreter, tclApi);

                                                created[1] = false; /* NOTE: Transferred. */

                                                //
                                                // NOTE: Create the TclBridge object to
                                                //       translate calls from the [eagle]
                                                //       Tcl command to the [eval] Eagle
                                                //       command.
                                                //
                                                tclBridge = TclBridge.Create(interpreter,
                                                    execute, ClientData.Empty, interp,
                                                    nativeCommandName, false, true, true,
                                                    ref result);

                                                created[2] = true; /* NOTE: We own it. */

                                                TraceOps.DebugTrace(String.Format(
                                                    "{0}: Startup: tclBridge {1}, " +
                                                    "interpreter = {2}, execute = {3}, " +
                                                    "interp = {4}, name = {5}, " +
                                                    "code = {6}, result = {7}{8}",
                                                    GlobalState.GetCurrentThreadId(),
                                                    (tclBridge != null) ? "created" :
                                                        "not created",
                                                    FormatOps.WrapOrNull(interpreter),
                                                    FormatOps.WrapOrNull(execute), interp,
                                                    FormatOps.WrapOrNull(nativeCommandName),
                                                    code, FormatOps.WrapOrNull(
                                                        true, true, result),
                                                    Environment.NewLine),
                                                    typeof(NativePackage).Name);

                                                if (tclBridge != null)
                                                {
                                                    string bridgeName =
                                                        FormatOps.TclBridgeName(
                                                            interpName, nativeCommandName);

                                                    interpreter.AddTclBridge(
                                                        bridgeName, tclBridge);

                                                    created[2] = false; /* NOTE: Transferred. */

                                                    if (AddInterpreter(
                                                            isolated ? interp : IntPtr.Zero,
                                                            interpreter))
                                                    {
                                                        created[0] = false; /* NOTE: Transferred. */
                                                    }
                                                }
                                                else
                                                {
                                                    code = ReturnCode.Error;
                                                }
                                            }
                                            else
                                            {
                                                result = String.Format(AlreadyAttachedError,
                                                    interp, FormatOps.WrapOrNull(interpreter));

                                                code = ReturnCode.Error;
                                            }
                                        }
                                        else
                                        {
                                            code = ReturnCode.Error;
                                        }
                                    }
                                }
                            }
                            else
                            {
                                result = String.Format(SafeUnsafeError,
                                    interp, FormatOps.WrapOrNull(interpreter));

                                code = ReturnCode.Error;
                            }
                        }
                        else if (code != ReturnCode.Error)
                        {
                            code = ReturnCode.Error;
                        }
                    }
                }
                finally
                {
                    //
                    // NOTE: If we fail for some reason, cleanup any resources
                    //       we allocated during this method call.
                    //
                    if (code != ReturnCode.Ok)
                    {
                        if (created[2] && (tclBridge != null))
                        {
                            tclBridge.Dispose();
                            tclBridge = null;
                        }

                        //
                        // NOTE: Only dispose of the Tcl API object if we
                        //       created it during this method call.
                        //
                        if (created[1] && (tclApi != null))
                        {
                            IDisposable disposable = tclApi as IDisposable;

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

                            tclApi = null;
                        }

                        //
                        // NOTE: Only dispose of the Eagle interpreter
                        //       if we created it during this method call;
                        //       otherwise, it may already be in use.
                        //
                        if (created[0] && (interpreter != null))
                        {
                            interpreter.Dispose();
                            interpreter = null;
                        }
                    }
                }
            }

            //
            // NOTE: We have no way of passing the result string back to
            //       native code; therefore, just "complain" about it
            //       (e.g. to the console).
            //
            if (code != ReturnCode.Ok)
                Complain(interp, isolated, code, result);

            TraceOps.DebugTrace(String.Format(
                "{0}: Startup: exited, protocolId = {1}, module = {2}, " +
                "stubs = {3}, interp = {4}, isolated = {5}, safe = {6}, " +
                "list = {7}, code = {8}, result = {9}{10}",
                GlobalState.GetCurrentThreadId(), FormatOps.WrapOrNull(true,
                true, protocolId), module, stubs, interp, isolated, safe,
                FormatOps.WrapOrNull(true, true, list), code,
                FormatOps.WrapOrNull(true, true, result),
                Environment.NewLine), typeof(NativePackage).Name);

            return (int)code;
        }

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

        //
        // WARNING: This method is used to integrate with native code via the
        //          native CLR API.
        //
        public static int Control(
            string argument /* This is the value of the "pwzArgument" argument
                             * as it was passed to native CLR API method
                             * ICLRRuntimeHost.ExecuteInDefaultAppDomain. */
            ) /* ENTRY-POINT, THREAD-SAFE */
        {
            ReturnCode code;
            string protocolId = null;
            IntPtr module = IntPtr.Zero;
            IntPtr stubs = IntPtr.Zero;
            IntPtr interp = IntPtr.Zero;
            bool isolated = false;
            bool safe = false;
            StringList list = null;
            Result result = null;

            TraceOps.DebugTrace(String.Format(
                "{0}: Control: entered, argument = {1}{2}",
                GlobalState.GetCurrentThreadId(),
                FormatOps.WrapOrNull(true, true, argument),
                Environment.NewLine), typeof(NativePackage).Name);

            code = ParseArgument(
                argument, ref protocolId, ref module, ref stubs, ref interp,
                ref isolated, ref safe, ref list, ref result);

            if (code == ReturnCode.Ok)
            {
                // do nothing.
            }

            //
            // NOTE: We have no way of passing the result string back to
            //       native code; therefore, just "complain" about it
            //       (e.g. to the console).
            //
            if (code != ReturnCode.Ok)
                Complain(interp, isolated, code, result);

            TraceOps.DebugTrace(String.Format(
                "{0}: Control: exited, protocolId = {1}, module = {2}, " +
                "stubs = {3}, interp = {4}, isolated = {5}, safe = {6}, " +
                "list = {7}, code = {8}, result = {9}{10}",
                GlobalState.GetCurrentThreadId(), FormatOps.WrapOrNull(true,
                true, protocolId), module, stubs, interp, isolated, safe,
                FormatOps.WrapOrNull(true, true, list), code,
                FormatOps.WrapOrNull(true, true, result),
                Environment.NewLine), typeof(NativePackage).Name);

            return (int)code;
        }

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

        //
        // WARNING: This method is used to integrate with native code via the
        //          native CLR API.
        //
        public static int Detach(
            string argument /* This is the value of the "pwzArgument" argument
                             * as it was passed to native CLR API method
                             * ICLRRuntimeHost.ExecuteInDefaultAppDomain. */
            ) /* ENTRY-POINT, THREAD-SAFE */
        {
            ReturnCode code;
            string protocolId = null;
            IntPtr module = IntPtr.Zero;
            IntPtr stubs = IntPtr.Zero;
            IntPtr interp = IntPtr.Zero;
            bool isolated = false;
            bool safe = false;
            StringList list = null;
            Result result = null;

            TraceOps.DebugTrace(String.Format(
                "{0}: Detach: entered, argument = {1}{2}",
                GlobalState.GetCurrentThreadId(),
                FormatOps.WrapOrNull(true, true, argument),
                Environment.NewLine), typeof(NativePackage).Name);

            code = ParseArgument(
                argument, ref protocolId, ref module, ref stubs, ref interp,
                ref isolated, ref safe, ref list, ref result);

            if (code == ReturnCode.Ok)
            {
                lock (syncRoot)
                {
                    try
                    {
                        Interpreter interpreter =
                            GetPrimaryOrIsolatedInterpreter(interp, isolated);

                        //
                        // NOTE: For an isolated Eagle interpreter, it should
                        //       be completely disposed.  For a shared Eagle
                        //       interpreter, its bridged Tcl commands should
                        //       be disposed.
                        //
                        if (isolated)
                        {
                            //
                            // NOTE: The package is being unloaded from this
                            //       Tcl interpreter; therefore, dispose of
                            //       this isolated Eagle interpreter now.
                            //
                            if (DisposeInterpreter(interp, interpreter))
                            {
                                code = ReturnCode.Ok;
                            }
                            else
                            {
                                result = String.Format(CouldNotDetachError,
                                    interp, FormatOps.WrapOrNull(interpreter));

                                code = ReturnCode.Error;
                            }
                        }
                        else
                        {
                            //
                            // NOTE: The package is being unloaded from this
                            //       Tcl interpreter.  Therefore, remove any
                            //       bridged Tcl commands from this shared
                            //       Eagle interpreter that are associated
                            //       with the Tcl interpreter.
                            //
                            code = interpreter.DisposeTclBridges(
                                interp, null, null, false, ref result);
                        }

                        if (code == ReturnCode.Ok)
                        {
                            //
                            // NOTE: Now that we are sure that either the
                            //       isolated Eagle interpreter has been
                            //       completely disposed -OR- the matching
                            //       bridged Tcl commands within the shared
                            //       Eagle interpreter have been disposed,
                            //       remove this Tcl interpreter from the
                            //       list [that we cannot delete] because
                            //       this is only an issue during disposal
                            //       of Eagle interpreters.
                            //
                            if (tclInterps != null)
                            {
                                tclInterps.RemoveAny(interp); /* O(N) */

                                if (tclInterps.Count == 0)
                                    tclInterps = null;
                            }
                        }
                    }
                    catch (Exception e)
                    {
                        result = e;
                        code = ReturnCode.Error;
                    }
                }
            }

            //
            // NOTE: We have no way of passing the result string back to
            //       native code; therefore, just "complain" about it
            //       (e.g. to the console).
            //
            if (code != ReturnCode.Ok)
                Complain(interp, isolated, code, result);

            TraceOps.DebugTrace(String.Format(
                "{0}: Detach: exited, protocolId = {1}, module = {2}, " +
                "stubs = {3}, interp = {4}, isolated = {5}, safe = {6}, " +
                "list = {7}, code = {8}, result = {9}{10}",
                GlobalState.GetCurrentThreadId(), FormatOps.WrapOrNull(true,
                true, protocolId), module, stubs, interp, isolated, safe,
                FormatOps.WrapOrNull(true, true, list), code,
                FormatOps.WrapOrNull(true, true, result),
                Environment.NewLine), typeof(NativePackage).Name);

            return (int)code;
        }

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

        //
        // WARNING: This method is used to integrate with native code via the
        //          native CLR API.
        //
        public static int Shutdown(
            string argument /* This is the value of the "pwzArgument" argument
                             * as it was passed to native CLR API method
                             * ICLRRuntimeHost.ExecuteInDefaultAppDomain. */
            ) /* ENTRY-POINT, THREAD-SAFE */
        {
            ReturnCode code;
            string protocolId = null;
            IntPtr module = IntPtr.Zero;
            IntPtr stubs = IntPtr.Zero;
            IntPtr interp = IntPtr.Zero;
            bool isolated = false;
            bool safe = false;
            StringList list = null;
            Result result = null;

            TraceOps.DebugTrace(String.Format(
                "{0}: Shutdown: entered, argument = {1}{2}",
                GlobalState.GetCurrentThreadId(),
                FormatOps.WrapOrNull(true, true, argument),
                Environment.NewLine), typeof(NativePackage).Name);

            code = ParseArgument(
                argument, ref protocolId, ref module, ref stubs, ref interp,
                ref isolated, ref safe, ref list, ref result);

            if (code == ReturnCode.Ok)
            {
                lock (syncRoot)
                {
                    try
                    {
                        //
                        // NOTE: The native package is being unloaded from
                        //       the entire process, dispose all the Eagle
                        //       interpreters now.
                        //
                        DisposeInterpreters(interp);

                        //
                        // NOTE: Now that we are sure the Eagle interpreters
                        //       have all been disposed, remove all the Tcl
                        //       interpreters from the list [that we should
                        //       not delete] because this is only an issue
                        //       during disposal of the Eagle interpreters.
                        //
                        if (tclInterps != null)
                        {
                            tclInterps.Clear();
                            tclInterps = null;
                        }
                    }
                    catch (Exception e)
                    {
                        result = e;
                        code = ReturnCode.Error;
                    }
                }
            }

            //
            // NOTE: We have no way of passing the result string back to
            //       native code; therefore, just "complain" about it
            //       (e.g. to the console).
            //
            if (code != ReturnCode.Ok)
                Complain(interp, isolated, code, result);

            TraceOps.DebugTrace(String.Format(
                "{0}: Shutdown: exited, protocolId = {1}, module = {2}, " +
                "stubs = {3}, interp = {4}, isolated = {5}, safe = {6}, " +
                "list = {7}, code = {8}, result = {9}{10}",
                GlobalState.GetCurrentThreadId(), FormatOps.WrapOrNull(true,
                true, protocolId), module, stubs, interp, isolated, safe,
                FormatOps.WrapOrNull(true, true, list), code,
                FormatOps.WrapOrNull(true, true, result),
                Environment.NewLine), typeof(NativePackage).Name);

            return (int)code;
        }
        #endregion
    }
}
