/*
 * Core.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;
using System.Resources;
using System.Text;
using System.Threading;
using Eagle._Attributes;
using Eagle._Components.Private;
using Eagle._Components.Public;
using Eagle._Constants;
using Eagle._Containers.Private;
using Eagle._Containers.Public;
using Eagle._Interfaces.Public;

#if !CONSOLE
using ConsoleColor = Eagle._Components.Public.ConsoleColor;
#endif

namespace Eagle._Hosts
{
    [ObjectId("0969beae-3d4a-42bf-b514-c7bc18bd6071")]
    public abstract class Core : Default, IDisposable, IGetInterpreter
    {
        #region Private Constants
        private const string NoColorPreferencesSuffix = "NoColor";

        private const string ForegroundColorSuffix = "ForegroundColor";
        private const string BackgroundColorSuffix = "BackgroundColor";

        private const string PropertyNameFormat =
            "SET {{{0}}}";

        private const string PropertyNameAndValueFormat =
            "SET {{{0}}} = {{{1}}}";

        private const string PropertyNameAndErrorFormat =
            "SET {{{0}}} --> {1}";

        private const string PropertyNameValueAndErrorFormat =
            "SET {{{0}}} = {{{2}}} --> {1}";

        private const string DefaultResourceBaseName = "Library";
        private const string NotFoundResourceName = "empty";
        #endregion

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

        #region Protected Constructors
        protected Core(
            IHostData hostData
            )
            : base(hostData)
        {
            if (hostData != null)
            {
                //
                // NOTE: This must be the name of the class that is deriving
                //       from us.  It is used to construct the file name for
                //       the host profile file.
                //
                typeName = hostData.TypeName;

                //
                // NOTE: Keep track of the interpreter that we are provided,
                //       if any.
                //
                interpreter = hostData.Interpreter;

                //
                // NOTE: Keep the resource manager provided by the custom
                //       IHost implementation, if any.
                //
                resourceManager = hostData.ResourceManager;
            }

            //
            // NOTE: Set the default resource base name.
            //
            resourceBaseName = DefaultResourceBaseName;

            //
            // BUGFIX: In case other host settings are loaded which affect
            //         the rest of the setup process, do this first.
            //
            /* IGNORED */
            LoadHostProfile(interpreter, this, GetType(),
                HostProfileFileEncoding, HostProfileFileName,
                NoProfile, false);

            /* IGNORED */
            SetupResources();

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

#if ISOLATED_PLUGINS
            //
            // NOTE: If this host is running in the same application domain
            //       as the parent interpreter, store this instance in the
            //       "coreHost" field of the interpreter for later use.
            //
            if (!SafeIsIsolated())
            {
                if (interpreter != null)
                {
                    lock (interpreter.SyncRoot)
                    {
                        interpreter.CoreHost = this;
                    }
                }
            }
#endif
        }
        #endregion

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

        #region Interpreter Support
        protected Interpreter SafeGetInterpreter()
        {
            try
            {
                return Interpreter; /* throw */
            }
            catch
            {
                // do nothing.
            }

            return null;
        }

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

#if ISOLATED_PLUGINS
        protected bool SafeIsIsolated()
        {
            try
            {
                if (!AppDomainOps.IsSame(Interpreter)) /* throw */
                    return true;
            }
            catch
            {
                // do nothing.
            }

            return false;
        }
#endif
        #endregion

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

        #region Host Title Support
        protected virtual string BuildCoreTitle(
            string packageName,
            Assembly assembly
            )
        {
            StringBuilder result = StringOps.NewStringBuilder();

            string release = FormatOps.ReleaseAttribute(
                AttributeOps.GetAssemblyRelease(
                    GlobalState.GetAssembly()));

            string[] values = {
                packageName, FormatOps.MajorMinor(
                    GlobalState.GetAssemblyVersion(),
                    Characters.v.ToString(), null),
                !String.IsNullOrEmpty(release) ? release :
                    AttributeOps.GetAssemblyTag(assembly),
                !String.IsNullOrEmpty(release) ? null :
                    FormatOps.PackageDateTime(
                        AttributeOps.GetAssemblyDateTime(
                            assembly)),
                FormatOps.AssemblyConfiguration(assembly,
                    Characters.OpenParenthesis.ToString(),
                    Characters.CloseParenthesis.ToString())
            };

            foreach (string value in values)
            {
                if (!String.IsNullOrEmpty(value))
                {
                    if (result.Length > 0)
                        result.Append(Characters.Space);

                    result.Append(value);
                }
            }

            return result.ToString();
        }
        #endregion

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

        #region Script File Support
        protected virtual IEnumerable<string> GetScriptResourceNames(
            string name,
            ScriptFlags flags,
            bool verbose
            )
        {
            bool skipQualified = FlagOps.HasFlags(flags,
                ScriptFlags.SkipQualified, true);

            bool skipNonQualified = FlagOps.HasFlags(flags,
                ScriptFlags.SkipNonQualified, true);

            bool skipRelative = FlagOps.HasFlags(flags,
                ScriptFlags.SkipRelative, true);

            bool skipRawName = FlagOps.HasFlags(flags,
                ScriptFlags.SkipRawName, true);

            bool skipFileName = FlagOps.HasFlags(flags,
                ScriptFlags.SkipFileName, true);

            bool preferDeepNames = FlagOps.HasFlags(flags,
                ScriptFlags.PreferDeepNames, true);

            string fileName = null;

            if ((!skipQualified || !skipRelative) && !skipFileName)
                fileName = FormatOps.ScriptTypeToFileName(name, true);

            string baseName = null;

            if ((!skipRawName || !skipFileName) && !skipNonQualified)
                baseName = Path.GetFileName(name);

            string baseFileName = null;

            if ((baseName != null) && !skipNonQualified && !skipFileName)
                baseFileName = FormatOps.ScriptTypeToFileName(baseName, true);

            PathComparisonType pathComparisonType = preferDeepNames ?
                PathComparisonType.DeepestFirst : PathComparisonType.Default;

            //
            // NOTE: Try the following ways to get the script via an embedded
            //       resource name, in order:
            //
            //       1. The provided name verbatim as a resource name, with and
            //          without a file extension.
            //
            //       2. Repeat step #1, treating the provided name as a fully
            //          qualified file name to be converted into a package
            //          relative file name, with and without a file extension.
            //
            //       3. Repeat step #1, treating the provided name as a fully
            //          qualified file name to be converted into a relative
            //          file name, with and without a file extension.
            //
            //       4. There is no step #4.
            //
            return new string[] {
                ///////////////////////////////////////////////////////////////
                // STEP #1
                ///////////////////////////////////////////////////////////////

                !skipQualified && !skipRawName ? name : null,
                !skipQualified && !skipFileName ? fileName : null,

                ///////////////////////////////////////////////////////////////
                // STEP #2
                ///////////////////////////////////////////////////////////////

                !skipRelative && !skipRawName ?
                    PackageOps.GetRelativeFileName(interpreter,
                        name, pathComparisonType, verbose) : null,
                !skipRelative && !skipFileName ?
                    PackageOps.GetRelativeFileName(interpreter,
                        fileName, pathComparisonType, verbose) : null,

                ///////////////////////////////////////////////////////////////
                // STEP #3
                ///////////////////////////////////////////////////////////////

                !skipNonQualified && !skipRawName ? baseName : null,
                !skipNonQualified && !skipFileName ? baseFileName : null
            };
        }
        #endregion

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

        #region Host Flags Support
        private bool PrivateResetFlags()
        {
            hostFlags = HostFlags.Invalid;

            return base.ResetFlags();
        }

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

        protected override void SetReadException(
            bool exception
            )
        {
            base.SetReadException(exception);
            hostFlags = HostFlags.Invalid;
        }

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

        protected override void SetWriteException(
            bool exception
            )
        {
            base.SetWriteException(exception);
            hostFlags = HostFlags.Invalid;
        }
        #endregion

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

        #region Profile Support
        protected internal virtual Encoding HostProfileFileEncoding
        {
            get
            {
                return null; /* NOTE: Use default. */
            }
        }

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

        protected internal virtual string HostProfileFileName
        {
            get
            {
                string packageName = GlobalState.GetPackageName();

                if (!String.IsNullOrEmpty(packageName))
                {
                    string profile = Profile; // NOTE: Grab property.

                    return PathOps.SearchForFile(interpreter, packageName + typeName +
                            (!String.IsNullOrEmpty(profile) ? profile : String.Empty) +
                            (NoColor ? NoColorPreferencesSuffix : String.Empty) +
                            FileExtension.Profile, FileSearchFlags.Standard);
                }

                return null;
            }
        }

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

        private static bool WritePropertyNameError(
            IDebugHost debugHost,
            string name,
            object value,
            Result error
            )
        {
            try
            {
                if (debugHost != null)
                    return debugHost.WriteResult(
                        ReturnCode.Error, String.Format(
                        (value != null) ?
                            PropertyNameValueAndErrorFormat : PropertyNameAndErrorFormat,
                        name, error, value), true);
            }
            catch
            {
                // do nothing.
            }

            return false;
        }

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

        private static bool WritePropertyNameException(
            IDebugHost debugHost,
            string name,
            object value,
            Exception exception
            )
        {
            try
            {
                if (debugHost != null)
                    return debugHost.WriteResult(
                        ReturnCode.Error, String.Format(
                        (value != null) ?
                            PropertyNameValueAndErrorFormat : PropertyNameAndErrorFormat,
                        name, exception, value), true);
            }
            catch
            {
                // do nothing.
            }

            return false;
        }

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

        private static bool WritePropertyError(
            IDebugHost debugHost,
            PropertyInfo propertyInfo,
            object value,
            Result error
            )
        {
            return WritePropertyNameError(debugHost, (propertyInfo != null) ?
                propertyInfo.Name : String.Empty, value, error);
        }

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

        private static bool WritePropertyException(
            IDebugHost debugHost,
            PropertyInfo propertyInfo,
            object value,
            Exception exception
            )
        {
            return WritePropertyNameException(debugHost, (propertyInfo != null) ?
                propertyInfo.Name : String.Empty, value, exception);
        }

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

        private static bool WritePropertyNameAndValue(
            IInteractiveHost interactiveHost,
            PropertyInfo propertyInfo,
            object value
            )
        {
            try
            {
                if (interactiveHost != null)
                    return interactiveHost.WriteLine(String.Format(
                        (value != null) ?
                            PropertyNameAndValueFormat : PropertyNameFormat,
                        (propertyInfo != null) ?
                            propertyInfo.Name : String.Empty,
                        value));
            }
            catch
            {
                // do nothing.
            }

            return false;
        }

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

        private static bool LoadHostProfile(
            Interpreter interpreter,
            IDebugHost debugHost,
            Type type,
            Encoding encoding,
            string fileName,
            bool noProfile,
            bool verbose
            )
        {
            Result error = null;

            return LoadHostProfile(
                interpreter, debugHost, type, encoding, fileName,
                noProfile, verbose, ref error);
        }

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

        internal static bool LoadHostProfile(
            Interpreter interpreter,
            IDebugHost debugHost,
            Type type,
            Encoding encoding,
            string fileName,
            bool noProfile, /* NOTE: Yes, this seems dumb. */
            bool verbose,
            ref Result error
            )
        {
            //
            // NOTE: Has loading the host profile been explicitly disabled?
            //
            if (!noProfile)
            {
                if (interpreter == null)
                {
                    error = "invalid interpreter";
                    return false;
                }

                if (debugHost == null)
                {
                    error = "interpreter host not available";
                    return false;
                }

                if (type == null)
                {
                    error = "invalid type";
                    return false;
                }

                if (String.IsNullOrEmpty(fileName))
                {
                    error = "invalid file name";
                    return false;
                }

                if (!File.Exists(fileName))
                {
                    error = String.Format(
                        "couldn't read file \"{0}\": no such file or directory",
                        fileName);

                    return false;
                }

                CultureInfo cultureInfo = interpreter.CultureInfo;
                BindingFlags bindingFlags = HostPropertyBindingFlags;

                //
                // NOTE: The encoding used here cannot be null; therefore,
                //       reset it to the default encoding associated with
                //       this method.
                //
                if (encoding == null)
                    encoding = Encoding.UTF8;

                foreach (string line in File.ReadAllLines(fileName, encoding))
                {
                    string trimLine = line.Trim();

                    if (!String.IsNullOrEmpty(trimLine))
                    {
                        if ((trimLine[0] != Characters.Comment) &&
                            (trimLine[0] != Characters.AltComment))
                        {
                            int index = trimLine.IndexOf(Characters.EqualSign);

                            if (index != Index.Invalid)
                            {
                                if ((index > 0) && ((index + 1) < trimLine.Length))
                                {
                                    string name = trimLine.Substring(0, index).Trim();
                                    string value = trimLine.Substring(index + 1).Trim();

                                    //
                                    // NOTE: We already know it is not null.
                                    //
                                    if (name.Length > 0)
                                    {
                                        PropertyInfo propertyInfo;

                                        try
                                        {
                                            propertyInfo = type.GetProperty(
                                                name, bindingFlags);
                                        }
                                        catch (Exception e)
                                        {
                                            if (verbose)
                                                WritePropertyNameException(
                                                    debugHost, name, null, e);

                                            //
                                            // NOTE: Skip further processing of this
                                            //       property line because we cannot
                                            //       look it up.
                                            //
                                            continue;
                                        }

                                        //
                                        // NOTE: Verify that the name is a valid
                                        //       ConsoleColor field.
                                        //
                                        if (propertyInfo != null)
                                        {
                                            Result localError = null;

                                            if (propertyInfo.PropertyType == typeof(ConsoleColor))
                                            {
                                                ResultList errors = new ResultList();

                                                //
                                                // NOTE: Verify that the value is a valid
                                                //       ConsoleColor Enum value.
                                                //
                                                object enumValue = EnumOps.TryParseEnum(
                                                    typeof(ConsoleColor), value, true,
                                                    ref localError);

                                                //
                                                // HACK: Support our custom color values (i.e. from
                                                //       the HostColor enumeration).  Currently, the
                                                //       only extra color we support is "None" and/or
                                                //       "Invalid", which both mean "do not change the
                                                //       color".
                                                //
                                                if (enumValue == null)
                                                {
                                                    errors.Add(localError);

                                                    enumValue = EnumOps.TryParseEnum(
                                                        typeof(HostColor), value, true,
                                                        ref localError);

                                                    if (enumValue is HostColor)
                                                        enumValue = (ConsoleColor)enumValue;
                                                    else
                                                        errors.Add(localError);
                                                }

                                                if (enumValue is ConsoleColor)
                                                {
                                                    try
                                                    {
                                                        //
                                                        // NOTE: Try to set the ConsoleColor
                                                        //       property.
                                                        //
                                                        if (propertyInfo.CanWrite)
                                                        {
                                                            propertyInfo.SetValue(
                                                                debugHost, enumValue, null);

                                                            if (verbose)
                                                                WritePropertyNameAndValue(
                                                                    debugHost, propertyInfo, enumValue);
                                                        }
                                                        else
                                                        {
                                                            WritePropertyError(
                                                                debugHost, propertyInfo, enumValue,
                                                                "property cannot be written");
                                                        }
                                                    }
                                                    catch (Exception e)
                                                    {
                                                        if (verbose)
                                                            WritePropertyException(
                                                                debugHost, propertyInfo, enumValue, e);
                                                    }
                                                }
                                                else if (verbose)
                                                {
                                                    localError = errors;

                                                    WritePropertyError(
                                                        debugHost, propertyInfo, value, localError);
                                                }
                                            }
                                            else if (propertyInfo.PropertyType == typeof(OutputStyle))
                                            {
                                                string oldValue = null;

                                                try
                                                {
                                                    if (propertyInfo.CanRead)
                                                    {
                                                        OutputStyle outputStyle = (OutputStyle)
                                                            propertyInfo.GetValue(debugHost, null);

                                                        oldValue = outputStyle.ToString();
                                                    }
                                                }
                                                catch (Exception e)
                                                {
                                                    if (verbose)
                                                        WritePropertyException(
                                                            debugHost, propertyInfo, null, e);
                                                }

                                                //
                                                // NOTE: Verify that the value is a valid
                                                //       OutputStyle Enum value.
                                                //
                                                object enumValue = EnumOps.TryParseFlagsEnum(
                                                    interpreter, typeof(OutputStyle), oldValue,
                                                    value, interpreter.CultureInfo, true, true,
                                                    ref localError);

                                                if (enumValue is OutputStyle)
                                                {
                                                    try
                                                    {
                                                        //
                                                        // NOTE: Try to set the OutputStyle
                                                        //       property.
                                                        //
                                                        if (propertyInfo.CanWrite)
                                                        {
                                                            propertyInfo.SetValue(
                                                                debugHost, enumValue, null);

                                                            if (verbose)
                                                                WritePropertyNameAndValue(
                                                                    debugHost, propertyInfo, enumValue);
                                                        }
                                                        else
                                                        {
                                                            WritePropertyError(
                                                                debugHost, propertyInfo, enumValue,
                                                                "property cannot be written");
                                                        }
                                                    }
                                                    catch (Exception e)
                                                    {
                                                        if (verbose)
                                                            WritePropertyException(
                                                                debugHost, propertyInfo, enumValue, e);
                                                    }
                                                }
                                                else if (verbose)
                                                {
                                                    WritePropertyError(
                                                        debugHost, propertyInfo, value, localError);
                                                }
                                            }
                                            else if (propertyInfo.PropertyType == typeof(HostStreamFlags))
                                            {
                                                string oldValue = null;

                                                try
                                                {
                                                    if (propertyInfo.CanRead)
                                                    {
                                                        HostStreamFlags HostStreamFlags = (HostStreamFlags)
                                                            propertyInfo.GetValue(debugHost, null);

                                                        oldValue = HostStreamFlags.ToString();
                                                    }
                                                }
                                                catch (Exception e)
                                                {
                                                    if (verbose)
                                                        WritePropertyException(
                                                            debugHost, propertyInfo, null, e);
                                                }

                                                //
                                                // NOTE: Verify that the value is a valid
                                                //       HostStreamFlags Enum value.
                                                //
                                                object enumValue = EnumOps.TryParseFlagsEnum(
                                                    interpreter, typeof(HostStreamFlags), oldValue,
                                                    value, interpreter.CultureInfo, true, true,
                                                    ref localError);

                                                if (enumValue is HostStreamFlags)
                                                {
                                                    try
                                                    {
                                                        //
                                                        // NOTE: Try to set the HostStreamFlags
                                                        //       property.
                                                        //
                                                        if (propertyInfo.CanWrite)
                                                        {
                                                            propertyInfo.SetValue(
                                                                debugHost, enumValue, null);

                                                            if (verbose)
                                                                WritePropertyNameAndValue(
                                                                    debugHost, propertyInfo, enumValue);
                                                        }
                                                        else
                                                        {
                                                            WritePropertyError(
                                                                debugHost, propertyInfo, enumValue,
                                                                "property cannot be written");
                                                        }
                                                    }
                                                    catch (Exception e)
                                                    {
                                                        if (verbose)
                                                            WritePropertyException(
                                                                debugHost, propertyInfo, enumValue, e);
                                                    }
                                                }
                                                else if (verbose)
                                                {
                                                    WritePropertyError(
                                                        debugHost, propertyInfo, value, localError);
                                                }
                                            }
                                            else if (propertyInfo.PropertyType == typeof(bool))
                                            {
                                                bool boolValue = false;

                                                if (Value.GetBoolean2(
                                                        value, ValueFlags.AnyBoolean,
                                                        cultureInfo, ref boolValue,
                                                        ref localError) == ReturnCode.Ok)
                                                {
                                                    try
                                                    {
                                                        //
                                                        // NOTE: Try to set the bool
                                                        //       property.
                                                        //
                                                        if (propertyInfo.CanWrite)
                                                        {
                                                            propertyInfo.SetValue(
                                                                debugHost, boolValue, null);

                                                            if (verbose)
                                                                WritePropertyNameAndValue(
                                                                    debugHost, propertyInfo, boolValue);
                                                        }
                                                        else
                                                        {
                                                            WritePropertyError(
                                                                debugHost, propertyInfo, boolValue,
                                                                "property cannot be written");
                                                        }
                                                    }
                                                    catch (Exception e)
                                                    {
                                                        if (verbose)
                                                            WritePropertyException(
                                                                debugHost, propertyInfo, boolValue, e);
                                                    }
                                                }
                                                else if (verbose)
                                                {
                                                    WritePropertyError(
                                                        debugHost, propertyInfo, value, localError);
                                                }
                                            }
                                            else if (propertyInfo.PropertyType == typeof(int))
                                            {
                                                int intValue = 0;

                                                if (Value.GetInteger2(
                                                        value, ValueFlags.AnyInteger,
                                                        cultureInfo, ref intValue,
                                                        ref localError) == ReturnCode.Ok)
                                                {
                                                    try
                                                    {
                                                        //
                                                        // NOTE: Try to set the int
                                                        //       property.
                                                        //
                                                        if (propertyInfo.CanWrite)
                                                        {
                                                            propertyInfo.SetValue(
                                                                debugHost, intValue, null);

                                                            if (verbose)
                                                                WritePropertyNameAndValue(
                                                                    debugHost, propertyInfo, intValue);
                                                        }
                                                        else
                                                        {
                                                            WritePropertyError(
                                                                debugHost, propertyInfo, intValue,
                                                                "property cannot be written");
                                                        }
                                                    }
                                                    catch (Exception e)
                                                    {
                                                        if (verbose)
                                                            WritePropertyException(
                                                                debugHost, propertyInfo, intValue, e);
                                                    }
                                                }
                                                else if (verbose)
                                                {
                                                    WritePropertyError(
                                                        debugHost, propertyInfo, value, localError);
                                                }
                                            }
                                            else if (propertyInfo.PropertyType == typeof(char))
                                            {
                                                //
                                                // HACK: Always grab the first character of the
                                                //       value string.
                                                //
                                                char charValue = !String.IsNullOrEmpty(value) ?
                                                    value[0] : Characters.Null;

                                                try
                                                {
                                                    //
                                                    // NOTE: Try to set the character
                                                    //       property.
                                                    //
                                                    if (propertyInfo.CanWrite)
                                                    {
                                                        propertyInfo.SetValue(
                                                            debugHost, charValue, null);

                                                        if (verbose)
                                                            WritePropertyNameAndValue(
                                                                debugHost, propertyInfo, charValue);
                                                    }
                                                    else
                                                    {
                                                        WritePropertyError(
                                                            debugHost, propertyInfo, charValue,
                                                            "property cannot be written");
                                                    }
                                                }
                                                catch (Exception e)
                                                {
                                                    if (verbose)
                                                        WritePropertyException(
                                                            debugHost, propertyInfo, charValue, e);
                                                }
                                            }
                                            else if (propertyInfo.PropertyType == typeof(string))
                                            {
                                                try
                                                {
                                                    //
                                                    // NOTE: Try to set the string
                                                    //       property.
                                                    //
                                                    if (propertyInfo.CanWrite)
                                                    {
                                                        propertyInfo.SetValue(
                                                            debugHost, value, null);

                                                        if (verbose)
                                                            WritePropertyNameAndValue(
                                                                debugHost, propertyInfo, value);
                                                    }
                                                    else
                                                    {
                                                        WritePropertyError(
                                                            debugHost, propertyInfo, value,
                                                            "property cannot be written");
                                                    }
                                                }
                                                catch (Exception e)
                                                {
                                                    if (verbose)
                                                        WritePropertyException(
                                                            debugHost, propertyInfo, value, e);
                                                }
                                            }
                                            else if (propertyInfo.PropertyType == typeof(StringList))
                                            {
                                                StringList listValue = null;

                                                //
                                                // WARNING: Cannot cache list representation
                                                //          here, the list may be modified via
                                                //          the public property in the future.
                                                //
                                                if (Parser.SplitList(
                                                        interpreter, value, 0, Length.Invalid,
                                                        false, ref listValue,
                                                        ref localError) == ReturnCode.Ok)
                                                {
                                                    try
                                                    {
                                                        //
                                                        // NOTE: Try to set the StringList
                                                        //       property.
                                                        //
                                                        if (propertyInfo.CanWrite)
                                                        {
                                                            propertyInfo.SetValue(
                                                                debugHost, listValue, null);

                                                            if (verbose)
                                                                WritePropertyNameAndValue(
                                                                    debugHost, propertyInfo, listValue);
                                                        }
                                                        else
                                                        {
                                                            WritePropertyError(
                                                                debugHost, propertyInfo, listValue,
                                                                "property cannot be written");
                                                        }
                                                    }
                                                    catch (Exception e)
                                                    {
                                                        if (verbose)
                                                            WritePropertyException(
                                                                debugHost, propertyInfo, listValue, e);
                                                    }
                                                }
                                                else if (verbose)
                                                {
                                                    WritePropertyError(
                                                        debugHost, propertyInfo, value, localError);
                                                }
                                            }
                                            else if (propertyInfo.PropertyType == typeof(Encoding))
                                            {
                                                Encoding encodingValue = null;

                                                if (Interpreter.GetEncoding(
                                                        interpreter, value, ref encodingValue,
                                                        ref localError) == ReturnCode.Ok)
                                                {
                                                    try
                                                    {
                                                        //
                                                        // NOTE: Try to set the Encoding
                                                        //       property.
                                                        //
                                                        if (propertyInfo.CanWrite)
                                                        {
                                                            propertyInfo.SetValue(
                                                                debugHost, encodingValue, null);

                                                            if (verbose)
                                                                WritePropertyNameAndValue(
                                                                    debugHost, propertyInfo, encodingValue);
                                                        }
                                                        else
                                                        {
                                                            WritePropertyError(
                                                                debugHost, propertyInfo, encodingValue,
                                                                "property cannot be written");
                                                        }
                                                    }
                                                    catch (Exception e)
                                                    {
                                                        if (verbose)
                                                            WritePropertyException(
                                                                debugHost, propertyInfo, encodingValue, e);
                                                    }
                                                }
                                                else if (verbose)
                                                {
                                                    WritePropertyError(
                                                        debugHost, propertyInfo, value, localError);
                                                }
                                            }
                                        }
                                        else if (verbose)
                                        {
                                            WritePropertyNameError(
                                                debugHost, name, null, "invalid host property name");
                                        }
                                    }
                                }
                            }
                        }
                    }
                }

                return true;
            }
            else
            {
                return true; /* NOTE: Fake success. */
            }
        }
        #endregion

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

        #region IGetInterpreter Members
        private Interpreter interpreter;
        public virtual Interpreter Interpreter
        {
            get { return interpreter; }
        }
        #endregion

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

        #region Protected Properties
        private string typeName;
        protected internal virtual string TypeName
        {
            get { return typeName; }
            internal set { typeName = value; }
        }

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

        private string resourceBaseName;
        protected virtual string ResourceBaseName
        {
            get { return resourceBaseName; }
            set { resourceBaseName = value; }
        }

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

        private ResourceManager coreResourceManager;
        protected virtual ResourceManager CoreResourceManager
        {
            get { return coreResourceManager; }
        }

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

        private ResourceManager resourceManager;
        protected virtual ResourceManager ResourceManager
        {
            get { return resourceManager; }
        }
        #endregion

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

        #region Script Resource Support
        private bool SetupResources()
        {
            try
            {
                //
                // NOTE: Create a resource manager for the embedded script
                //       library, if any.
                //
                coreResourceManager = new ResourceManager(ResourceBaseName,
                    GlobalState.GetAssembly());

                //
                // NOTE: Now, since creating it will pretty much always succeed,
                //       we need to test it to make sure it is really available.
                //
                /* IGNORED */
                coreResourceManager.GetString(NotFoundResourceName); /* throw */

                //
                // NOTE: If we get this far, the resource manager is created and
                //       functional.
                //
                return true;
            }
            catch
            {
                //
                // NOTE: The resource manager we created does not appear to work,
                //       null it out so that it will not be used later.
                //
                if (coreResourceManager != null)
                    coreResourceManager = null;
            }

            return false;
        }
        #endregion

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

        #region IInteractiveHost Members
        public override ReturnCode Prompt(
            PromptType type,
            ref PromptFlags flags,
            ref Result error
            )
        {
            CheckDisposed();

            flags &= ~PromptFlags.Done;

            if (interpreter == null)
            {
                error = "invalid interpreter";
                return ReturnCode.Error;
            }

            //
            // BUGFIX: Always bypass the interpreter readiness checks here;
            //         otherwise, we can get into very nasty situations (e.g.
            //         infinite recursion for [debug oncancel], etc).
            //
            ReturnCode code;
            Result value = null;

            if ((type != PromptType.None) &&
                interpreter.GetVariableValue(VariableFlags.ViaPrompt,
                    (type == PromptType.Continue) ?
                        TclVars.Prompt2 : TclVars.Prompt1,
                    ref value) == ReturnCode.Ok)
            {
                Result result = null;
                int errorLine = 0;

                code = interpreter.EvaluatePromptScript(value, ref result,
                    ref errorLine);

                if (code == ReturnCode.Ok)
                {
                    //
                    // NOTE: The prompt script probably displayed some kind
                    //       of prompt; therefore, we are done.
                    //
                    flags |= PromptFlags.Done;
                }
                else
                {
                    //
                    // NOTE: Attempt to show the error from the prompt script.
                    //
                    /* IGNORED */
                    WriteResultLine(code, result, errorLine);

                    //
                    // NOTE: Add error information to the interpreter.
                    //
                    Engine.AddErrorInformation(
                        interpreter, result, String.Format(
                            "{0}    (script that generates prompt, line {1})",
                            Environment.NewLine, errorLine));

                    //
                    // NOTE: Now, transfer the prompt script evaluation error
                    //       to the caller.
                    //
                    error = result;
                }
            }
            else
            {
                //
                // NOTE: Either our caller requested a prompt type of "None"
                //       -OR- there is no prompt script configured.  So far,
                //       this has been a complete success.
                //
                code = ReturnCode.Ok;
            }

            //
            // NOTE: If we did not evaluate a prompt script -OR- if that script
            //       failed then we attempt to output the appropriate default
            //       prompt.
            //
            if ((value == null) || (code != ReturnCode.Ok))
            {
                //
                // NOTE: Now, we need to fallback to the default
                //       prompt.
                //
                string prompt = HostOps.GetDefaultPrompt(type, flags);

                //
                // NOTE: If we got a valid default prompt for this
                //       type, attempt to write it now.
                //
                if ((prompt != null) && Write(prompt))
                {
                    //
                    // NOTE: We displayed the debug prompt for this
                    //       type.
                    //
                    flags |= PromptFlags.Done;
                }
            }

            return code;
        }

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

        private HostFlags hostFlags = HostFlags.Invalid;
        public override HostFlags GetFlags()
        {
            CheckDisposed();

            if (hostFlags == HostFlags.Invalid)
            {
                //
                // NOTE: We support the "Prompt", "CreateThread",
                //       "QueueWorkItem", "Sleep", "Yield",
                //       "GetStream", and "GetScript" methods of
                //       the IEngineHost interface.
                //
                hostFlags = HostFlags.Prompt | HostFlags.Thread |
                            HostFlags.WorkItem | HostFlags.Stream |
                            HostFlags.Script | HostFlags.Sleep |
                            HostFlags.Yield | base.GetFlags();

#if ISOLATED_PLUGINS
                //
                // NOTE: If this host is not running in the same
                //       application domain as the parent interpreter,
                //       also add the "Isolated" flag.
                //
                if (SafeIsIsolated())
                    hostFlags |= HostFlags.Isolated;
#endif
            }

            return hostFlags;
        }
        #endregion

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

        #region IEngineHost Members
        public override ReturnCode CreateThread(
            ThreadStart start,
            int maxStackSize,
            bool userInterface,
            bool isBackground,
            ref Thread thread,
            ref Result error
            )
        {
            CheckDisposed();

            try
            {
                thread = Engine.CreateThread(
                    start, maxStackSize, userInterface, isBackground);

                return ReturnCode.Ok;
            }
            catch (Exception e)
            {
                error = e;
            }

            return ReturnCode.Error;
        }

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

        public override ReturnCode CreateThread(
            ParameterizedThreadStart start,
            int maxStackSize,
            bool userInterface,
            bool isBackground,
            ref Thread thread,
            ref Result error
            )
        {
            CheckDisposed();

            try
            {
                thread = Engine.CreateThread(
                    start, maxStackSize, userInterface, isBackground);

                return ReturnCode.Ok;
            }
            catch (Exception e)
            {
                error = e;
            }

            return ReturnCode.Error;
        }

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

        public override ReturnCode QueueWorkItem(
            WaitCallback callback,
            object state,
            ref Result error
            )
        {
            CheckDisposed();

            try
            {
                if (Engine.QueueWorkItem(callback, state))
                    return ReturnCode.Ok;
                else
                    error = "could not queue work item";
            }
            catch (Exception e)
            {
                error = e;
            }

            return ReturnCode.Error;
        }

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

        public override ReturnCode GetStream(
            string path,
            FileMode mode,
            FileAccess access,
            FileShare share,
            int bufferSize,
            FileOptions options,
            ref HostStreamFlags flags,
            ref string fullPath,
            ref Stream stream,
            ref Result error
            )
        {
            CheckDisposed();

            try
            {
                return RuntimeOps.NewStream(
                    interpreter, path, mode, access, share, bufferSize,
                    options, ref flags, ref fullPath, ref stream, ref error);
            }
            catch (Exception e)
            {
                error = e;
            }

            return ReturnCode.Error;
        }

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

        public override ReturnCode GetScript(
            string name,
            ref ScriptFlags flags,
            ref Result result
            )
        {
            CheckDisposed();

            //
            // NOTE: The purpose of this routine is to redirect requests for library
            //       scripts made by the script engine to our internal resources (i.e.
            //       so that the scripts do not have to exist elsewhere on the file
            //       system).
            //
            if (interpreter != null)
            {
                //
                // NOTE: Make sure the script name is valid.
                //
                if (name != null)
                {
                    //
                    // NOTE: First, if it has not been prohibited by the caller,
                    //       try to get the requested script externally, using
                    //       our standard file system search routine.
                    //
                    string value = null;

                    if (!FlagOps.HasFlags(flags, ScriptFlags.NoFileSystem, true))
                    {
                        FileSearchFlags fileSearchFlags = FileSearchFlags.Default;

                        if (FlagOps.HasFlags(flags, ScriptFlags.Mapped, true))
                            fileSearchFlags |= FileSearchFlags.Mapped;

                        if (FlagOps.HasFlags(flags, ScriptFlags.AutoSourcePath, true))
                            fileSearchFlags |= FileSearchFlags.AutoSourcePath;

                        if (FlagOps.HasFlags(flags, ScriptFlags.User, true))
                            fileSearchFlags |= FileSearchFlags.User;

                        if (FlagOps.HasFlags(flags, ScriptFlags.Application, true))
                            fileSearchFlags |= FileSearchFlags.Application;

                        if (FlagOps.HasFlags(flags, ScriptFlags.Vendor, true))
                            fileSearchFlags |= FileSearchFlags.Vendor;

                        if (FlagOps.HasFlags(flags, ScriptFlags.StrictGetFile, true))
                            fileSearchFlags |= FileSearchFlags.Strict;

                        if (FlagOps.HasFlags(flags, ScriptFlags.SearchDirectory, true))
                            fileSearchFlags |= FileSearchFlags.DirectoryLocation;

                        if (FlagOps.HasFlags(flags, ScriptFlags.SearchFile, true))
                            fileSearchFlags |= FileSearchFlags.FileLocation;

                        value = PathOps.SearchForFile(interpreter, name,
                            fileSearchFlags);
                    }

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

                    if (value != null)
                    {
                        flags |= ScriptFlags.File;
                        result = value;

                        return ReturnCode.Ok;
                    }
                    else if (!FlagOps.HasFlags(flags, ScriptFlags.NoResources, true))
                    {
                        //
                        // NOTE: These are the counts for how many tries were
                        //       performed using the plugins and/or resource
                        //       managers.
                        //
                        int[] counts = { 0, 0 };

                        //
                        // NOTE: This is the list of errors encountered during
                        //       the search for this script.
                        //
                        ResultList errors = null;

                        //
                        // NOTE: Does the caller wish to skip treating the name
                        //       as the raw resource name and/or a file name?
                        //       Also, does the caller wish to skip the qualified
                        //       and/or non-qualified name?
                        //
                        bool quiet = FlagOps.HasFlags(flags,
                            ScriptFlags.Quiet, true);

                        bool verbose = FlagOps.HasFlags(flags,
                            ScriptFlags.Verbose, true);

                        IEnumerable<string> resourceNames = GetScriptResourceNames(
                            name, flags, verbose);

                        //
                        // NOTE: Create a string dictionary with the resource
                        //       names so that we do not search needlessly for
                        //       duplicates.
                        //
                        StringDictionary uniqueResourceNames = new StringDictionary();

                        if (resourceNames != null)
                        {
                            foreach (string resourceName in resourceNames)
                            {
                                if ((resourceName != null) &&
                                    !uniqueResourceNames.ContainsKey(resourceName))
                                {
                                    uniqueResourceNames.Add(resourceName, null);
                                }
                            }
                        }

                        //
                        // NOTE: Grab the engine flags as we need them for the
                        //       calls into the engine.
                        //
                        EngineFlags engineFlags = interpreter.EngineFlags;

#if XML
                        if (FlagOps.HasFlags(flags, ScriptFlags.NoXml, true))
                            engineFlags |= EngineFlags.NoXml;
#endif

                        //
                        // NOTE: See if we are allowed to search for the script
                        //       via plugin resource strings.
                        //
                        PluginWrapperDictionary plugins = null;

                        //
                        // HACK: We must be in the same application domain for
                        //       this to work properly.
                        //
                        if (!FlagOps.HasFlags(flags, ScriptFlags.NoPlugins, true))
                        {
                            plugins = interpreter.CopyPlugins();

                            if (plugins != null)
                            {
                                CultureInfo cultureInfo = interpreter.CultureInfo;

                                foreach (KeyValuePair<string, _Wrappers.Plugin> pair in plugins)
                                {
                                    IPlugin plugin = pair.Value;

                                    //
                                    // HACK: Skip all the static system (i.e. "core") plugins.
                                    //
                                    if ((plugin != null) &&
                                        !FlagOps.HasFlags(plugin.Flags, PluginFlags.System, true))
                                    {
                                        string pluginName = FormatOps.PluginSimpleName(plugin);

                                        foreach (string uniqueResourceName in uniqueResourceNames.Keys)
                                        {
                                            string resourceValue;
                                            Result error = null;

                                            if (pluginName != null)
                                            {
                                                resourceValue = plugin.GetString(interpreter,
                                                    pluginName + Characters.Period + uniqueResourceName,
                                                    cultureInfo, ref error);

                                                counts[0]++;

                                                if (resourceValue != null)
                                                {
                                                    using (StringReader stringReader =
                                                            new StringReader(resourceValue)) /* throw */
                                                    {
                                                        string text = null;

                                                        if (Engine.ReadScriptStream(
                                                                interpreter, name, stringReader,
                                                                0, Count.Invalid, ref engineFlags,
                                                                ref text, ref result) == ReturnCode.Ok)
                                                        {
                                                            result = text;
                                                            return ReturnCode.Ok;
                                                        }
                                                        else
                                                        {
                                                            return ReturnCode.Error;
                                                        }
                                                    }
                                                }
                                                else if (verbose && (error != null))
                                                {
                                                    if (errors == null)
                                                        errors = new ResultList();

                                                    /* VERBOSE */
                                                    errors.Add(error);
                                                }
                                            }

                                            resourceValue = plugin.GetString(interpreter,
                                                uniqueResourceName, cultureInfo, ref error);

                                            counts[0]++;

                                            if (resourceValue != null)
                                            {
                                                using (StringReader stringReader =
                                                        new StringReader(resourceValue)) /* throw */
                                                {
                                                    string text = null;

                                                    if (Engine.ReadScriptStream(
                                                            interpreter, name, stringReader,
                                                            0, Count.Invalid, ref engineFlags,
                                                            ref text, ref result) == ReturnCode.Ok)
                                                    {
                                                        result = text;
                                                        return ReturnCode.Ok;
                                                    }
                                                    else
                                                    {
                                                        return ReturnCode.Error;
                                                    }
                                                }
                                            }
                                            else if (verbose && (error != null))
                                            {
                                                if (errors == null)
                                                    errors = new ResultList();

                                                /* VERBOSE */
                                                errors.Add(error);
                                            }
                                        }
                                    }
                                }
                            }
                        }

                        //
                        // NOTE: In order to use the interpreter resource manager,
                        //       we must be in the same application domain.  We
                        //       should always be able to use our own resource
                        //       manager.
                        //
                        ResourceManager thisResourceManager = this.ResourceManager;

                        //
                        // NOTE: If this host is running isolated (i.e. in an
                        //       isolated application domain, via a plugin), skip
                        //       using the resource manager from the interpreter
                        //       because it cannot be marshalled from the other
                        //       application domain (it's a private field).
                        //
                        ResourceManager interpreterResourceManager =
#if ISOLATED_PLUGINS
                            !SafeIsIsolated() ? interpreter.ResourceManager : null;
#else
                            interpreter.ResourceManager;
#endif

                        if (!FlagOps.HasFlags(flags, ScriptFlags.NoResourceManager, true))
                        {
                            //
                            // NOTE: We prefer to use the plugin provided resource
                            //       manager, then our local resource manager, and
                            //       finally the resource manager for the interpreter
                            //       that we are associated with, which may contain
                            //       library scripts.
                            //
                            ResourceManager[] resourceManagers = new ResourceManager[] {
                                thisResourceManager, coreResourceManager,
                                interpreterResourceManager
                            };

                            foreach (ResourceManager resourceManager in resourceManagers)
                            {
                                if (resourceManager != null)
                                {
                                    foreach (string uniqueResourceName in uniqueResourceNames.Keys)
                                    {
                                        try
                                        {
                                            //
                                            // NOTE: Attempt to fetch the script as a
                                            //       resource stream.
                                            //
                                            Stream resourceStream = resourceManager.GetStream(
                                                uniqueResourceName); /* throw */

                                            counts[1]++;

                                            //
                                            // NOTE: In order to continue, we must have
                                            //       the found the resource stream
                                            //       associated with the named resource.
                                            //
                                            if (resourceStream != null)
                                            {
                                                using (StreamReader streamReader =
                                                        new StreamReader(resourceStream)) /* throw */
                                                {
                                                    string text = null;

                                                    if (Engine.ReadScriptStream(
                                                            interpreter, name, streamReader,
                                                            0, Count.Invalid, ref engineFlags,
                                                            ref text, ref result) == ReturnCode.Ok)
                                                    {
                                                        result = text;
                                                        return ReturnCode.Ok;
                                                    }
                                                    else
                                                    {
                                                        return ReturnCode.Error;
                                                    }
                                                }
                                            }
                                        }
                                        catch (InvalidOperationException)
                                        {
                                            //
                                            // NOTE: If we get to this point, it means that
                                            //       the resource does exist; however, it
                                            //       cannot be accessed via stream.  Attempt
                                            //       to fetch the script as a resource string.
                                            //
                                            string resourceValue = resourceManager.GetString(
                                                uniqueResourceName); /* throw */

                                            counts[1]++;

                                            //
                                            // NOTE: In order to continue, we must have
                                            //       the found the resource stream
                                            //       associated with the named resource.
                                            //
                                            if (resourceValue != null)
                                            {
                                                using (StringReader stringReader =
                                                        new StringReader(resourceValue)) /* throw */
                                                {
                                                    string text = null;

                                                    if (Engine.ReadScriptStream(
                                                            interpreter, name, stringReader,
                                                            0, Count.Invalid, ref engineFlags,
                                                            ref text, ref result) == ReturnCode.Ok)
                                                    {
                                                        result = text;
                                                        return ReturnCode.Ok;
                                                    }
                                                    else
                                                    {
                                                        return ReturnCode.Error;
                                                    }
                                                }
                                            }
                                        }
                                        catch (Exception e)
                                        {
                                            if (verbose)
                                            {
                                                if (errors == null)
                                                    errors = new ResultList();

                                                /* VERBOSE */
                                                errors.Add(e);
                                            }

                                            if (FlagOps.HasFlags(flags,
                                                    ScriptFlags.ErrorOnException, true))
                                            {
                                                break;
                                            }
                                        }
                                    }
                                }
                            }
                        }

                        if (errors == null)
                            errors = new ResultList();

                        /* NOT VERBOSE */
                        errors.Insert(0, String.Format(
                            "script \"{0}\" not found",
                            name));

                        //
                        // NOTE: In quiet mode, skip the other error information.
                        //
                        if (!quiet)
                        {
                            if (plugins == null)
                                /* NOT VERBOSE */
                                errors.Add("invalid plugin list");

                            if (counts[0] == 0)
                                /* NOT VERBOSE */
                                errors.Add("no plugin was queried");

                            if (thisResourceManager == null)
                                /* NOT VERBOSE */
                                errors.Add("invalid extension resource manager");

                            if (coreResourceManager == null)
                                /* NOT VERBOSE */
                                errors.Add("invalid core resource manager");

                            if (interpreterResourceManager == null)
                                /* NOT VERBOSE */
                                errors.Add("invalid interpreter resource manager");

                            if (counts[1] == 0)
                                /* NOT VERBOSE */
                                errors.Add("no resource manager was queried");
                        }

                        result = errors;
                    }
                    else
                    {
                        result = String.Format(
                            "script \"{0}\" not found",
                            name);
                    }
                }
                else
                {
                    result = "invalid script name";
                }
            }
            else
            {
                result = "invalid interpreter";
            }

            return ReturnCode.Error;
        }

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

        public override bool Sleep(
            int milliseconds
            )
        {
            CheckDisposed();

            try
            {
                HostOps.Sleep(milliseconds);

                return true;
            }
            catch
            {
                return false;
            }
        }

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

        public override bool Yield()
        {
            CheckDisposed();

            try
            {
                HostOps.Yield();

                return true;
            }
            catch
            {
                return false;
            }
        }
        #endregion

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

        #region IDebugHost Members
        public override IHost Clone()
        {
            CheckDisposed();

            return Clone(interpreter);
        }
        #endregion

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

        #region IHost Members
        public override string DefaultTitle
        {
            get
            {
                CheckDisposed();

                try
                {
                    if (base.DefaultTitle == null)
                    {
                        string packageName = GlobalState.GetPackageName();

                        if (!String.IsNullOrEmpty(packageName))
                        {
                            Assembly assembly = GlobalState.GetAssembly();
                            base.DefaultTitle = BuildCoreTitle(packageName, assembly);
                        }
                    }

                    return base.DefaultTitle;
                }
                catch
                {
                    // do nothing.
                }

                return null;
            }
        }

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

        public override bool ResetFlags()
        {
            CheckDisposed();

            return PrivateResetFlags();
        }

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

        public override ReturnCode GetColors(
            string theme, /* RESERVED */
            string name,
            bool foreground,
            bool background,
            ref ConsoleColor foregroundColor,
            ref ConsoleColor backgroundColor,
            ref Result error
            )
        {
            ReturnCode code = ReturnCode.Ok;

            //
            // NOTE: Only the "default" theme (i.e. using null or an empty
            //       string for the configuration) is supported for now.
            //
            if (String.IsNullOrEmpty(theme))
            {
                if (!String.IsNullOrEmpty(name))
                {
                    try
                    {
                        ConsoleColor localForegroundColor = DefaultForegroundColor;
                        ConsoleColor localBackgroundColor = DefaultBackgroundColor;

                        //
                        // NOTE: Did they request the foreground color?
                        //
                        if ((code == ReturnCode.Ok) && foreground)
                        {
                            PropertyInfo property = base.GetType().GetProperty(
                                String.Format("{0}{1}", name, ForegroundColorSuffix),
                                HostPropertyBindingFlags, null, typeof(ConsoleColor),
                                Type.EmptyTypes, null);

                            if (property != null)
                            {
                                if (property.CanRead)
                                {
                                    localForegroundColor = (ConsoleColor)property.GetValue(
                                        this, null);
                                }
                                else
                                {
                                    error = String.Format(
                                        "property for foreground color \"{0}\" cannot be read",
                                        name);

                                    code = ReturnCode.Error;
                                }
                            }
                            else
                            {
                                error = String.Format(
                                    "property for foreground color \"{0}\" not found",
                                    name);

                                code = ReturnCode.Error;
                            }
                        }

                        //
                        // NOTE: Did they request the background color?
                        //
                        if ((code == ReturnCode.Ok) && background)
                        {
                            PropertyInfo property = base.GetType().GetProperty(
                                String.Format("{0}{1}", name, BackgroundColorSuffix),
                                HostPropertyBindingFlags, null, typeof(ConsoleColor),
                                Type.EmptyTypes, null);

                            if (property != null)
                            {
                                if (property.CanRead)
                                {
                                    localBackgroundColor = (ConsoleColor)property.GetValue(
                                        this, null);
                                }
                                else
                                {
                                    error = String.Format(
                                        "property for background color \"{0}\" cannot be read",
                                        name);

                                    code = ReturnCode.Error;
                                }
                            }
                            else
                            {
                                error = String.Format(
                                    "property for background color \"{0}\" not found",
                                    name);

                                code = ReturnCode.Error;
                            }
                        }

                        //
                        // NOTE: If we succeeded in looking up the requested colors,
                        //       return them now.
                        //
                        if (code == ReturnCode.Ok)
                        {
                            foregroundColor = localForegroundColor;
                            backgroundColor = localBackgroundColor;
                        }
                    }
                    catch (Exception e)
                    {
                        error = e;
                        code = ReturnCode.Error;
                    }
                }
                else
                {
                    error = "invalid color name";
                    code = ReturnCode.Error;
                }
            }
            else
            {
                error = "unsupported theme name";
                code = ReturnCode.Error;
            }

            return code;
        }

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

        public override ReturnCode SetColors(
            string theme, /* RESERVED */
            string name,
            bool foreground,
            bool background,
            ConsoleColor foregroundColor,
            ConsoleColor backgroundColor,
            ref Result error
            )
        {
            ReturnCode code = ReturnCode.Ok;

            //
            // NOTE: Only the "default" theme (i.e. using null or an empty
            //       string for the configuration) is supported for now.
            //
            if (String.IsNullOrEmpty(theme))
            {
                if (!String.IsNullOrEmpty(name))
                {
                    try
                    {
                        //
                        // NOTE: Did they request the foreground color?
                        //
                        if ((code == ReturnCode.Ok) && foreground)
                        {
                            PropertyInfo property = base.GetType().GetProperty(
                                String.Format("{0}{1}", name, ForegroundColorSuffix),
                                HostPropertyBindingFlags, null, typeof(ConsoleColor),
                                Type.EmptyTypes, null);

                            if (property != null)
                            {
                                if (property.CanWrite)
                                {
                                    property.SetValue(this, foregroundColor, null);
                                }
                                else
                                {
                                    error = String.Format(
                                        "property for foreground color \"{0}\" cannot be written",
                                        name);

                                    code = ReturnCode.Error;
                                }
                            }
                            else
                            {
                                error = String.Format(
                                    "property for foreground color \"{0}\" not found",
                                    name);

                                code = ReturnCode.Error;
                            }
                        }

                        //
                        // NOTE: Did they request the background color?
                        //
                        if ((code == ReturnCode.Ok) && background)
                        {
                            PropertyInfo property = base.GetType().GetProperty(
                                String.Format("{0}{1}", name, BackgroundColorSuffix),
                                HostPropertyBindingFlags, null, typeof(ConsoleColor),
                                Type.EmptyTypes, null);

                            if (property != null)
                            {
                                if (property.CanWrite)
                                {
                                    property.SetValue(this, backgroundColor, null);
                                }
                                else
                                {
                                    error = String.Format(
                                        "property for background color \"{0}\" cannot be written",
                                        name);

                                    code = ReturnCode.Error;
                                }
                            }
                            else
                            {
                                error = String.Format(
                                    "property for background color \"{0}\" not found",
                                    name);

                                code = ReturnCode.Error;
                            }
                        }
                    }
                    catch (Exception e)
                    {
                        error = e;
                        code = ReturnCode.Error;
                    }
                }
                else
                {
                    error = "invalid color name";
                    code = ReturnCode.Error;
                }
            }
            else
            {
                error = "unsupported theme name";
                code = ReturnCode.Error;
            }

            return code;
        }

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

        public override ReturnCode Reset(
            ref Result error
            )
        {
            CheckDisposed();

            if (base.Reset(ref error) == ReturnCode.Ok)
            {
                if (!PrivateResetFlags()) /* NON-VIRTUAL */
                {
                    error = "failed to reset flags";
                    return ReturnCode.Error;
                }

                return ReturnCode.Ok;
            }

            return ReturnCode.Error;
        }
        #endregion

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

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

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

        protected override void Dispose(bool disposing)
        {
            try
            {
                if (!disposed)
                {
                    //if (disposing)
                    //{
                    //    ////////////////////////////////////
                    //    // dispose managed resources here...
                    //    ////////////////////////////////////
                    //}

                    //////////////////////////////////////
                    // release unmanaged resources here...
                    //////////////////////////////////////
                }
            }
            finally
            {
                base.Dispose(disposing);

                disposed = true;
            }
        }
        #endregion
    }
}
