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

using System;
using System.Collections.Generic;
using System.Threading;
using Eagle._Attributes;
using Eagle._Components.Public;
using Eagle._Components.Public.Delegates;
using Eagle._Containers.Public;
using Eagle._Interfaces.Public;
using _Public = Eagle._Components.Public;

namespace Eagle._Components.Private
{
    [ObjectId("5f2b9883-f5da-4d3c-85b8-cddb6b0de9f8")]
    internal sealed class Namespace :
#if ISOLATED_INTERPRETERS || ISOLATED_PLUGINS
        ScriptMarshalByRefObject,
#endif
        IIdentifier, INamespace, IDisposable
    {
        #region Private Data
        private Dictionary<string, INamespace> children;
        private ObjectDictionary imports;
        #endregion

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

        #region Private Constructors
        private Namespace()
        {
            kind = IdentifierKind.Namespace;
            id = AttributeOps.GetObjectId(this);
            children = new Dictionary<string, INamespace>();
            imports = new ObjectDictionary();
            exportNames = new StringDictionary();
        }
        #endregion

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

        #region Public Constructors
        public Namespace(
            INamespaceData namespaceData
            )
            : this()
        {
            if (namespaceData != null)
            {
                name = namespaceData.Name;
                clientData = namespaceData.ClientData;
                interpreter = namespaceData.Interpreter;
                parent = namespaceData.Parent;
                variableFrame = namespaceData.VariableFrame;
                unknown = namespaceData.Unknown;
            }
        }
        #endregion

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

        #region IIdentifier Members
        private IdentifierKind kind;
        public IdentifierKind Kind
        {
            get { CheckDisposed(); return kind; }
            set { CheckDisposed(); kind = value; }
        }

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

        private Guid id;
        public Guid Id
        {
            get { CheckDisposed(); return id; }
            set { CheckDisposed(); id = value; }
        }

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

        private string name;
        public string Name
        {
            get { CheckDisposed(); return name; }
            set
            {
                CheckDisposed();

                string oldName;
                bool global;

                BeforeNameChange(out oldName, out global);

                name = value;

                AfterNameChange(oldName, global);
            }
        }

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

        private string group;
        public string Group
        {
            get { CheckDisposed(); return group; }
            set { CheckDisposed(); group = value; }
        }

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

        private string description;
        public string Description
        {
            get { CheckDisposed(); return description; }
            set { CheckDisposed(); description = value; }
        }
        #endregion

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

        #region IHaveClientData Members
        private IClientData clientData;
        public IClientData ClientData
        {
            get { CheckDisposed(); return clientData; }
            set { CheckDisposed(); clientData = value; }
        }
        #endregion

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

        #region IGetInterpreter / ISetInterpreter Members
        private Interpreter interpreter;
        public Interpreter Interpreter /* READ-ONLY */
        {
            get { CheckDisposed(); return interpreter; }
            set { CheckDisposed(); throw new NotSupportedException(); }
        }
        #endregion

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

        #region INamespaceData Members
        private INamespace parent;
        public INamespace Parent /* READ-ONLY */
        {
            get { CheckDisposed(); return parent; }
            set { CheckDisposed(); throw new NotSupportedException(); }
        }

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

        private ICallFrame variableFrame;
        public ICallFrame VariableFrame /* READ-ONLY */
        {
            get { CheckDisposed(); return variableFrame; }
            set { CheckDisposed(); throw new NotSupportedException(); }
        }

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

        private string unknown;
        public string Unknown
        {
            get { CheckDisposed(); return unknown; }
            set { CheckDisposed(); unknown = value; }
        }
        #endregion

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

        #region INamespace Members
        private string qualifiedName;
        public string QualifiedName
        {
            get
            {
                CheckDisposed();

                return GetQualifiedName();
            }
        }

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

        private int referenceCount;
        public int ReferenceCount
        {
            get { CheckDisposed(); return referenceCount; }
        }

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

        private bool deleted;
        public bool Deleted
        {
            get { CheckDisposed(); return deleted; }
        }

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

        public ReturnCode GetImport(
            string qualifiedImportName,
            ref string qualifiedExportName,
            ref Result error
            )
        {
            CheckDisposed();

            if (qualifiedImportName == null)
            {
                error = "invalid import name";
                return ReturnCode.Error;
            }

            if (!NamespaceOps.IsQualifiedName(qualifiedImportName))
            {
                error = "import name must be qualified";
                return ReturnCode.Error;
            }

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

            if (imports == null)
            {
                error = String.Format(
                    "imports not available in namespace \"{0}\"",
                    GetDisplayName());

                return ReturnCode.Error;
            }

            object @object;

            if (imports.TryGetValue(qualifiedImportName, out @object))
            {
                IAlias alias = @object as IAlias;

                if (alias != null)
                {
                    qualifiedExportName = NamespaceOps.GetAliasName(alias);
                    return ReturnCode.Ok;
                }
                else
                {
                    error = String.Format(
                        "import name \"{0}\" is not an alias",
                        qualifiedImportName);
                }
            }
            else
            {
                error = String.Format(
                    "import name \"{0}\" not found",
                    qualifiedImportName);
            }

            return ReturnCode.Error;
        }

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

        public ReturnCode AddImport(
            INamespace targetNamespace,
            string qualifiedImportName,
            string qualifiedExportName,
            ref Result error
            )
        {
            CheckDisposed();

            if (qualifiedImportName == null)
            {
                error = "invalid import name";
                return ReturnCode.Error;
            }

            if (qualifiedExportName == null)
            {
                error = "invalid export name";
                return ReturnCode.Error;
            }

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

            if (imports == null)
            {
                error = String.Format(
                    "imports not available in namespace \"{0}\"",
                    GetDisplayName());

                return ReturnCode.Error;
            }

            IAlias alias = null;
            Result localResult = null;

            if (interpreter.AddAlias(
                    qualifiedImportName, CommandFlags.None,
                    AliasFlags.NamespaceImport, _Public.ClientData.Empty,
                    interpreter, null, new ArgumentList(qualifiedExportName),
                    null, 0, ref alias, ref localResult) == ReturnCode.Ok)
            {
                alias.SourceNamespace = this;
                alias.TargetNamespace = targetNamespace;
            }
            else
            {
                error = localResult;
                return ReturnCode.Error;
            }

            string nameToken = alias.NameToken;

            if (nameToken == null)
            {
                //
                // NOTE: This should not happen as the alias was successfully
                //       added to the interpreter and the name token cannot be
                //       null in that case.
                //
                error = "invalid alias name";
                return ReturnCode.Error;
            }

            if (imports.ContainsKey(qualifiedImportName))
            {
                Result localError = null;
                ResultList errors = new ResultList();

                errors.Add(String.Format(
                    "can't add import \"{0}\" in \"{1}\": already exists",
                    nameToken, GetDisplayName()));

                if (interpreter.RemoveAliasAndCommand(
                        nameToken, null, false,
                        ref localError) != ReturnCode.Ok)
                {
                    errors.Add(localError);
                }

                error = errors;
                return ReturnCode.Error;
            }

            imports.Add(qualifiedImportName, alias);
            return ReturnCode.Ok;
        }

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

        public ReturnCode RenameImport(
            string oldQualifiedName,
            string newQualifiedName,
            bool strict,
            ref Result error
            )
        {
            CheckDisposed();

            if (imports == null)
            {
                error = String.Format(
                    "imports not available in namespace \"{0}\"",
                    GetDisplayName());

                return ReturnCode.Error;
            }

            INamespace oldNamespace = NamespaceOps.LookupParent(
                interpreter, oldQualifiedName, false, true, false, ref error);

            if (oldNamespace == null)
                return ReturnCode.Error;

            INamespace newNamespace = NamespaceOps.LookupParent(
                interpreter, newQualifiedName, false, true, false, ref error);

            if (newNamespace == null)
                return ReturnCode.Error;

            int count = 0;

            ObjectDictionary localImports = new ObjectDictionary(
                (IDictionary<string, object>)imports);

            foreach (KeyValuePair<string, object> pair in localImports)
            {
                IAlias alias = pair.Value as IAlias;

                if (alias == null)
                    continue;

                string aliasName = NamespaceOps.GetAliasName(alias);

                if (NamespaceOps.IsSame(
                        alias.TargetNamespace, oldNamespace) &&
                    StringOps.Match(interpreter, MatchMode.Glob, aliasName,
                        ScriptOps.MakeCommandName(oldQualifiedName), false))
                {
                    alias.TargetNamespace = newNamespace;
                    NamespaceOps.SetAliasName(alias, newQualifiedName);

                    return ReturnCode.Ok;
                }
                else if (strict)
                {
                    error = String.Format(
                        "import \"{0}\" is not an alias in namespace \"{1}\"",
                        oldQualifiedName, GetDisplayName());

                    return ReturnCode.Error;
                }
            }

            if (strict && (count == 0))
            {
                error = String.Format(
                    "no imports matched name \"{0}\" in namespace \"{1}\"",
                    oldQualifiedName, GetDisplayName());

                return ReturnCode.Error;
            }

            return ReturnCode.Ok;
        }

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

        public ReturnCode RemoveImports(
            string qualifiedPattern,
            bool strict,
            ref Result error
            )
        {
            CheckDisposed();

            if (imports == null)
            {
                error = String.Format(
                    "imports not available in namespace \"{0}\"",
                    GetDisplayName());

                return ReturnCode.Error;
            }

            INamespace patternNamespace = null;

            if ((qualifiedPattern != null) && (interpreter != null))
            {
                patternNamespace = NamespaceOps.LookupParent(
                    interpreter, qualifiedPattern, false, true, false,
                    ref error);

                if (patternNamespace == null)
                    return ReturnCode.Error;
            }

            int count = 0;

            ObjectDictionary localImports = new ObjectDictionary(
                (IDictionary<string, object>)imports);

            foreach (KeyValuePair<string, object> pair in localImports)
            {
                IAlias alias = pair.Value as IAlias;
                string aliasName = NamespaceOps.GetAliasName(alias);
                string originName = GetOriginName(aliasName);

                if ((qualifiedPattern == null) ||
                    StringOps.Match(interpreter, MatchMode.Glob, aliasName,
                        ScriptOps.MakeCommandName(qualifiedPattern), false) ||
                    ((originName != null) &&
                    StringOps.Match(interpreter, MatchMode.Glob, originName,
                        ScriptOps.MakeCommandName(qualifiedPattern), false)))
                {
                    INamespace originNamespace = null;

                    if (originName != null)
                    {
                        originNamespace = NamespaceOps.LookupParent(
                            interpreter, originName, false, true, false,
                            ref error);

                        if (originNamespace == null)
                            return ReturnCode.Error;
                    }

                    if ((patternNamespace == null) || ((alias != null) &&
                        (NamespaceOps.IsSame(
                            alias.TargetNamespace, patternNamespace) ||
                        NamespaceOps.IsSame(
                            originNamespace, patternNamespace))))
                    {
                        if ((alias != null) && (interpreter != null))
                        {
                            string nameToken = alias.NameToken;

                            if ((nameToken != null) &&
                                interpreter.RemoveAliasAndCommand(
                                    alias.NameToken, null, false,
                                    ref error) != ReturnCode.Ok)
                            {
                                return ReturnCode.Error;
                            }
                        }

                        count += imports.Remove(pair.Key) ? 1 : 0;
                    }
                }
            }

            if (strict && (count == 0))
            {
                error = String.Format(
                    "no imports matched pattern \"{0}\" in namespace \"{1}\"",
                    qualifiedPattern, GetDisplayName());

                return ReturnCode.Error;
            }

            return ReturnCode.Ok;
        }

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

        public StringList GetImportNames(
            string pattern,
            bool keys,
            bool tailOnly
            )
        {
            CheckDisposed();

            StringList list = new StringList();

            if (imports == null)
                return list;

            if (pattern != null)
                pattern = ScriptOps.MakeCommandName(pattern);

            foreach (KeyValuePair<string, object> pair in imports)
            {
                IAlias alias = pair.Value as IAlias;

                if (alias == null)
                    continue;

                if (keys)
                {
                    string importName = pair.Key;

                    string importNameTailOnly = NamespaceOps.TailOnly(
                        importName);

                    if ((pattern == null) || StringOps.Match(
                            interpreter, MatchMode.Glob, importNameTailOnly,
                            pattern, false))
                    {
                        list.Add(tailOnly ? importNameTailOnly : importName);
                    }
                }
                else
                {
                    string aliasName = NamespaceOps.GetAliasName(alias);

                    string aliasNameTailOnly = NamespaceOps.TailOnly(
                        aliasName);

                    if ((pattern == null) || StringOps.Match(
                            interpreter, MatchMode.Glob, aliasNameTailOnly,
                            pattern, false))
                    {
                        list.Add(tailOnly ? aliasNameTailOnly : aliasName);
                    }
                }
            }

            return list;
        }

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

        private StringDictionary exportNames;
        public StringDictionary ExportNames
        {
            get { CheckDisposed(); return exportNames; }
        }

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

        public StringList GetExportNames(
            string pattern
            )
        {
            CheckDisposed();

            return (exportNames != null) ?
                new StringList(exportNames.Keys).ToList(pattern, false)
                as StringList : new StringList();
        }

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

        public int Enter(
            bool all
            )
        {
            CheckDisposed();

            int count = 0;

            if (all)
            {
                INamespace @namespace = parent;

                while (@namespace != null)
                {
                    count += @namespace.Enter(false);
                    @namespace = @namespace.Parent;
                }
            }

            count += Interlocked.Increment(ref referenceCount);
            return count;
        }

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

        public int Exit(
            bool all
            )
        {
            CheckDisposed();

            int count = 0;

            if (all)
            {
                INamespace @namespace = parent;

                while (@namespace != null)
                {
                    count += @namespace.Exit(false);
                    @namespace = @namespace.Parent;
                }
            }

            count += Interlocked.Decrement(ref referenceCount);
            return count;
        }

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

        public void MarkDeleted()
        {
            CheckDisposed();

            deleted = true;
        }

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

        public INamespace GetChild(
            string name,
            ref Result error
            )
        {
            CheckDisposed();

            if (String.IsNullOrEmpty(name))
            {
                error = "cannot lookup child: invalid name";
                return null;
            }

            if (children == null)
            {
                error = String.Format(
                    "children not available in namespace \"{0}\"",
                    GetDisplayName());

                return null;
            }

            INamespace child;

            if (children.TryGetValue(name, out child))
                return child;

            error = String.Format(
                "namespace \"{0}\" not found in \"{1}\"",
                name, GetDisplayName());

            return null;
        }

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

        public ReturnCode AddChild(
            INamespace @namespace,
            ref Result error
            )
        {
            CheckDisposed();

            if (@namespace == null)
            {
                error = "cannot add child: invalid namespace";
                return ReturnCode.Error;
            }

            if (children == null)
            {
                error = String.Format(
                    "children not available in namespace \"{0}\"",
                    GetDisplayName());

                return ReturnCode.Error;
            }

            string name = @namespace.Name;

            if (String.IsNullOrEmpty(name))
            {
                error = "cannot add child: invalid name";
                return ReturnCode.Error;
            }

            if (children.ContainsKey(name))
            {
                error = String.Format(
                    "can't add \"{0}\": namespace already exists",
                    name);

                return ReturnCode.Error;
            }

            children.Add(name, @namespace);
            return ReturnCode.Ok;
        }

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

        public ReturnCode RenameChild(
            string oldName,
            string newName,
            ref Result error
            )
        {
            CheckDisposed();

            if (String.IsNullOrEmpty(oldName))
            {
                error = "cannot rename child: invalid old name";
                return ReturnCode.Error;
            }

            if (String.IsNullOrEmpty(newName))
            {
                error = "cannot rename child: invalid new name";
                return ReturnCode.Error;
            }

            if (children == null)
            {
                error = String.Format(
                    "children not available in namespace \"{0}\"",
                    GetDisplayName());

                return ReturnCode.Error;
            }

            INamespace child;

            if (!children.TryGetValue(oldName, out child))
            {
                error = String.Format(
                    "can't rename from \"{0}\": namespace does not exist",
                    oldName);

                return ReturnCode.Error;
            }

            if (children.ContainsKey(newName))
            {
                error = String.Format(
                    "can't rename to \"{0}\": namespace already exists",
                    newName);

                return ReturnCode.Error;
            }

            children.Add(newName, child);
            children.Remove(oldName);

            return ReturnCode.Ok;
        }

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

        public ReturnCode RemoveChild(
            string name,
            ref Result error
            )
        {
            CheckDisposed();

            if (String.IsNullOrEmpty(name))
            {
                error = "cannot remove child: invalid name";
                return ReturnCode.Error;
            }

            if (children == null)
            {
                error = String.Format(
                    "children not available in namespace \"{0}\"",
                    GetDisplayName());

                return ReturnCode.Error;
            }

            if (!children.Remove(name))
            {
                error = String.Format(
                    "can't remove \"{0}\": namespace does not exist",
                    name);

                return ReturnCode.Error;
            }

            return ReturnCode.Ok;
        }

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

        public ReturnCode Traverse(
            NamespaceCallback callback,
            IClientData clientData,
            ref Result error
            )
        {
            CheckDisposed();

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

            ReturnCode code;

            code = callback(this, clientData, ref error);

            if (code != ReturnCode.Ok)
                return code;

            if (children == null)
                return code;

            if (children.Count == 0)
                return code;

            foreach (KeyValuePair<string, INamespace> pair in children)
            {
                INamespace child = pair.Value;

                if (child == null)
                    continue;

                code = child.Traverse(callback, clientData, ref error);

                if (code != ReturnCode.Ok)
                    return code;
            }

            return code;
        }

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

        public IEnumerable<INamespace> GetChildren(
            string pattern,
            bool deleted
            ) /* CANNOT RETURN NULL */
        {
            CheckDisposed();

            return new List<INamespace>(
                PrivateGetChildren(pattern, deleted).Values);
        }
        #endregion

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

        #region Private Methods
        private bool IsGlobal()
        {
            return (parent == null);
        }

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

        private string GetQualifiedName()
        {
            if (qualifiedName == null)
                qualifiedName = NamespaceOps.GetQualifiedName(this, null);

            return qualifiedName;
        }

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

        private void ResetQualifiedName()
        {
            qualifiedName = null;
        }

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

        private string GetDisplayName()
        {
            return GetQualifiedName();
        }

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

        private string GetOriginName(
            string aliasName
            )
        {
            if (aliasName == null)
                return null;

            Result result = null;

            if (NamespaceOps.Origin(interpreter, this,
                    NamespaceOps.MakeAbsoluteName(aliasName),
                    ref result) == ReturnCode.Ok)
            {
                return ScriptOps.MakeCommandName(result);
            }

            return null;
        }

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

        private void BeforeNameChange(
            out string oldName,
            out bool global
            )
        {
            //
            // NOTE: Save the original name for later, we will need it.
            //
            oldName = name;

            //
            // NOTE: Is this the global namespace?
            //
            global = IsGlobal();

            //
            // HACK: Force figuring out the qualified name now, prior to
            //       the local name being changed, if necessary.
            //
            if (global)
                /* IGNORED */
                GetQualifiedName();
        }

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

        private void AfterNameChange(
            string oldName,
            bool global
            )
        {
            //
            // HACK: The qualified name of the global namespace cannot be
            //       changed; however, we let the local name be changed.
            //
            if (!global)
            {
                ResetQualifiedName();

                //
                // NOTE: For the rest of the steps in here, we want to make
                //       sure that the name is actually different now.
                //
                if (!NamespaceOps.IsSame(oldName, name))
                {
                    //
                    // HACK: Next, force all child namespaces to recompute
                    //       their qualified names as well.
                    //
                    ResetChildNames(null, true);

                    //
                    // HACK: Finally, "notify" the parent namespace that our
                    //       name has been changed (i.e. so it can update its
                    //       list of children).
                    //
                    if (parent != null)
                    {
                        ReturnCode renameCode;
                        Result renameError = null;

                        renameCode = parent.RenameChild(
                            oldName, name, ref renameError);

                        if (renameCode != ReturnCode.Ok)
                        {
                            DebugOps.Complain(
                                interpreter, renameCode, renameError);
                        }
                    }
                }
            }
        }

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

        private IDictionary<string, INamespace> PrivateGetChildren(
            string pattern,
            bool deleted
            ) /* CANNOT RETURN NULL */
        {
            IDictionary<string, INamespace> dictionary =
                new Dictionary<string, INamespace>();

            if (children != null)
            {
                if (pattern != null)
                {
                    bool qualified = NamespaceOps.IsQualifiedName(pattern);

                    foreach (KeyValuePair<string, INamespace> pair in children)
                    {
                        INamespace child = pair.Value;

                        if (child == null)
                            continue;

                        if (!deleted && child.Deleted)
                            continue;

                        if (qualified)
                        {
                            if (!StringOps.Match(
                                    interpreter, MatchMode.Glob,
                                    child.QualifiedName, pattern, false))
                            {
                                continue;
                            }
                        }
                        else
                        {
                            if (!StringOps.Match(
                                    interpreter, MatchMode.Glob,
                                    pair.Key, pattern, false))
                            {
                                continue;
                            }
                        }

                        dictionary.Add(pair.Key, child);
                    }
                }
                else
                {
                    foreach (KeyValuePair<string, INamespace> pair in children)
                    {
                        INamespace child = pair.Value;

                        if (child == null)
                            continue;

                        if (!deleted && child.Deleted)
                            continue;

                        dictionary.Add(pair.Key, child);
                    }
                }
            }

            return dictionary;
        }

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

        private void ResetChildNames(
            string pattern,
            bool deleted
            )
        {
            foreach (KeyValuePair<string, INamespace> pair in
                    PrivateGetChildren(pattern, deleted))
            {
                INamespace child = pair.Value;

                if (child == null)
                    continue;

                //
                // HACK: Force the qualified name to be recomputed.
                //
                child.Name = child.Name;
            }
        }
        #endregion

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

        #region System.Object Overrides
        public override string ToString()
        {
            CheckDisposed();

            return name;
        }
        #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(Namespace));
#endif
        }

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

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

                    if (children != null)
                    {
                        foreach (INamespace child in children.Values)
                        {
                            if (child == null)
                                continue;

                            IDisposable disposable = child as IDisposable;

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

                        children.Clear();
                        children = null;
                    }

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

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

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

                    if (imports != null)
                    {
                        ReturnCode removeCode;
                        Result removeError = null;

                        removeCode = RemoveImports(
                            null, false, ref removeError);

                        if (removeCode != ReturnCode.Ok)
                        {
                            DebugOps.Complain(
                                interpreter, removeCode, removeError);
                        }
                    }

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

                    if (interpreter != null)
                        interpreter = null; /* NOT OWNED */

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

                    if (variableFrame != null)
                    {
                        variableFrame.Free(true);
                        variableFrame = null;
                    }

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

                    qualifiedName = null;
                    parent = null; /* NOT OWNED */
                    referenceCount = 0;
                    deleted = false;
                    unknown = null;

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

                    kind = IdentifierKind.None;
                    id = Guid.Empty;
                    name = null;
                    group = null;
                    description = null;
                    clientData = null;
                }

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

                disposed = true;
            }
        }
        #endregion

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

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

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

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