/*
 * DebugOps.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.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Threading;
using Eagle._Attributes;
using Eagle._Components.Public;
using Eagle._Containers.Public;
using Eagle._Interfaces.Public;

namespace Eagle._Components.Private
{
    [ObjectId("1d388444-db3b-41b5-a23e-b25084d1c94b")]
    internal static class DebugOps
    {
        #region Public Constants
        public static readonly string DefaultCategory =
            System.Diagnostics.Debugger.DefaultCategory;
        #endregion

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

        #region Private Constants
        private static readonly string TextWriteExceptionFormat =
            "write of text failed ({0}): {1}{2}";

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

        private static readonly string HostWriteExceptionFormat =
            "write to host failed ({0}): {1}{2}";
        #endregion

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

        #region Private Data
        //
        // NOTE: The current number of calls to Complain() that are active.
        //       This number should always be zero or one.
        //
        private static int complainLevels = 0;
        #endregion

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

        #region Private Stack Trace Methods
        [MethodImpl(MethodImplOptions.NoInlining)]
        private static StackTrace GetStackTrace(
            int skipFrames
            )
        {
            //
            // NOTE: Always skip this method.
            //
            return new StackTrace(skipFrames + 1, true);
        }

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

        [MethodImpl(MethodImplOptions.NoInlining)]
        private static string GetStackTraceString()
        {
            StackTrace stackTrace = GetStackTrace(1);

            if (stackTrace != null)
                return stackTrace.ToString();

            return null;
        }

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

        [MethodImpl(MethodImplOptions.NoInlining)]
        private static string GetMethodName(
            int skipFrames,
            StringList skipNames
            )
        {
            string result = null;

            try
            {
                //
                // NOTE: Create a new stack trace based on the current
                //       execution stack.
                //
                StackTrace stackTrace = GetStackTrace(0);

                if (stackTrace != null)
                {
                    //
                    // NOTE: Always skip this method (i.e. we start with at
                    //       least one, not zero).
                    //
                    int count = stackTrace.FrameCount;

                    for (int index = skipFrames + 1; index < count; index++)
                    {
                        //
                        // NOTE: Get the stack frame for the current index.
                        //
                        StackFrame stackFrame = stackTrace.GetFrame(index);

                        //
                        // NOTE: Get the method for this stack frame.
                        //
                        MethodBase methodBase = stackFrame.GetMethod();

                        //
                        // NOTE: Get the type for this method.
                        //
                        Type type = methodBase.DeclaringType;

                        //
                        // NOTE: Format the method name with its full type
                        //       name.
                        //
                        string methodFullName = FormatOps.MethodFullName(
                            type, methodBase.Name);

                        //
                        // NOTE: Format the method name with its type name.
                        //
                        string methodName = FormatOps.MethodName(
                            type, methodBase.Name);

                        //
                        // NOTE: Do we need to skip this method (based on
                        //       the name and/or the type qualified method
                        //       name)?
                        //
                        if ((skipNames == null) ||
                            (!skipNames.Contains(methodBase.Name) &&
                             !skipNames.Contains(methodFullName) &&
                             !skipNames.Contains(methodName)))
                        {
                            //
                            // NOTE: Return the method name formatted with
                            //       the declaring type.
                            //
                            result = FormatOps.MethodFullName(
                                type, methodBase.Name);

                            break;
                        }
                    }
                }
            }
            catch
            {
                // do nothing.
            }

            return result;
        }

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

        [MethodImpl(MethodImplOptions.NoInlining)]
        private static string GetMethodName(
            MethodBase skipMethod
            )
        {
            string skipMethodName = null;

            if (skipMethod != null)
                skipMethodName = FormatOps.MethodName(
                    skipMethod.DeclaringType, skipMethod.Name);

            string callerMethodName = GetMethodName(1, null);

            //
            // NOTE: We are doing this on behalf of the direct caller;
            //       therefore, skip this method AND the calling method.
            //
            return GetMethodName(2,
                new StringList(callerMethodName, skipMethodName));
        }
        #endregion

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

        #region Public Stack Trace Methods
        [MethodImpl(MethodImplOptions.NoInlining)]
        public static MethodBase GetMethod(
            int skipFrames
            )
        {
            try
            {
                //
                // NOTE: Create a new stack trace based on the current
                //       execution stack.
                //
                StackTrace stackTrace = GetStackTrace(0);

                if (stackTrace != null)
                {
                    //
                    // NOTE: Always skip this method (i.e. we start with at
                    //       least one, not zero).
                    //
                    StackFrame stackFrame = stackTrace.GetFrame(
                        skipFrames + 1);

                    //
                    // NOTE: Get the method for this stack frame.
                    //
                    return stackFrame.GetMethod();
                }
            }
            catch
            {
                // do nothing.
            }

            return null;
        }
        #endregion

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

        #region Private Interpreter Helper Methods
        private static long GetComplaintId()
        {
            return GlobalState.NextId();
        }

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

        private static bool SafeGetQuiet(
            Interpreter interpreter,
            bool @default
            )
        {
            if (interpreter != null)
            {
                //
                // BUGFIX: The interpreter may have been disposed and we do
                //         not want to throw an exception; therefore, wrap
                //         the interpreter property access in a try block.
                //
                bool locked = false;

                try
                {
                    interpreter.TryLock(ref locked);

                    if (locked)
                        return interpreter.Quiet; /* throw */
                }
                catch
                {
                    // do nothing.
                }
                finally
                {
                    interpreter.ExitLock(ref locked);
                }
            }

            //
            // HOOK: Allow the test suite (and others components) to override
            //       the quietness setting even if the interpreter is not
            //       available (or has already been disposed).
            //
            if (CommonOps.Environment.GetVariable(EnvVars.Quiet) != null)
                return true;

            return @default;
        }

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

        public static bool SafeGetTraceToHost(
            Interpreter interpreter,
            bool @default
            )
        {
            if (interpreter != null)
            {
                //
                // BUGFIX: The interpreter may have been disposed and we do
                //         not want to throw an exception; therefore, wrap
                //         the interpreter property access in a try block.
                //
                bool locked = false;

                try
                {
                    interpreter.TryLock(ref locked);

                    if (locked)
                    {
                        return FlagOps.HasFlags(
                            interpreter.InterpreterFlags,
                            InterpreterFlags.TraceToHost, true);
                    }
                }
                catch
                {
                    // do nothing.
                }
                finally
                {
                    interpreter.ExitLock(ref locked);
                }
            }

            if (CommonOps.Environment.GetVariable(EnvVars.TraceToHost) != null)
                return true;

            return @default;
        }

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

        private static bool SafeGetTraceStack(
            Interpreter interpreter,
            bool @default
            )
        {
            if (interpreter != null)
            {
                //
                // BUGFIX: The interpreter may have been disposed and we do
                //         not want to throw an exception; therefore, wrap
                //         the interpreter property access in a try block.
                //
                bool locked = false;

                try
                {
                    interpreter.TryLock(ref locked);

                    if (locked)
                    {
                        return FlagOps.HasFlags(
                            interpreter.InterpreterFlags,
                            InterpreterFlags.TraceStack, true);
                    }
                }
                catch
                {
                    // do nothing.
                }
                finally
                {
                    interpreter.ExitLock(ref locked);
                }
            }

            if (CommonOps.Environment.GetVariable(EnvVars.TraceStack) != null)
                return true;

            return @default;
        }

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

        private static TextWriter SafeGetDebugTextWriter(
            Interpreter interpreter
            )
        {
            if (interpreter != null)
            {
                //
                // BUGFIX: The interpreter may have been disposed and we do
                //         not want to throw an exception; therefore, wrap
                //         the interpreter property access in a try block.
                //
                bool locked = false;

                try
                {
                    interpreter.TryLock(ref locked);

                    if (locked)
                        return interpreter.DebugTextWriter; /* throw */
                }
                catch
                {
                    // do nothing.
                }
                finally
                {
                    interpreter.ExitLock(ref locked);
                }
            }

            return null;
        }

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

        private static IDebugHost SafeGetHost(
            Interpreter interpreter
            )
        {
            if (interpreter != null)
            {
                //
                // BUGFIX: The interpreter may have been disposed and we do
                //         not want to throw an exception; therefore, wrap
                //         the interpreter property access in a try block.
                //
                bool locked = false;

                try
                {
                    interpreter.TryLock(ref locked);

                    if (locked)
                        return interpreter.Host; /* throw */
                }
                catch
                {
                    // do nothing.
                }
                finally
                {
                    interpreter.ExitLock(ref locked);
                }
            }

            return null;
        }

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

        public static string SafeGetComplaint(
            Interpreter interpreter
            )
        {
            if (interpreter != null)
            {
                //
                // BUGFIX: The interpreter may have been disposed and we do
                //         not want to throw an exception; therefore, wrap
                //         the interpreter property access in a try block.
                //
                bool locked = false;

                try
                {
                    interpreter.TryLock(ref locked);

                    if (locked)
                        return interpreter.Complaint; /* throw */
                }
                catch
                {
                    // do nothing.
                }
                finally
                {
                    interpreter.ExitLock(ref locked);
                }
            }

            return null;
        }

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

        public static void SafeSetComplaint(
            Interpreter interpreter,
            string complaint
            )
        {
            if (interpreter != null)
            {
                //
                // BUGFIX: The interpreter may have been disposed and we do
                //         not want to throw an exception; therefore, wrap
                //         the interpreter property access in a try block.
                //
                bool locked = false;

                try
                {
                    interpreter.TryLock(ref locked);

                    if (locked)
                        interpreter.Complaint = complaint; /* throw */
                }
                catch
                {
                    // do nothing.
                }
                finally
                {
                    interpreter.ExitLock(ref locked);
                }
            }
        }
        #endregion

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

        #region Private Output Support Methods
#if NATIVE
        public static void Output(
            string message
            )
        {
            NativeOps.OutputDebugMessage(String.Format(
                "{0}{1}", message, Environment.NewLine));
        }
#endif

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

        private static void WriteWithoutFail(
            string value
            )
        {
#if NATIVE && DEBUG
            Output(value);
#endif

#if DEBUG
            DebugWriteLine(value, null);
#endif

#if TRACE
            TraceWriteLine(value, null);
#endif
        }

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

        private static void TextWriteException(
            long id,
            Exception e
            )
        {
            WriteWithoutFail(String.Format(
                TextWriteExceptionFormat, id, e, Environment.NewLine));
        }

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

        private static void HostWriteException(
            long id,
            Exception e
            )
        {
            WriteWithoutFail(String.Format(
                HostWriteExceptionFormat, id, e, Environment.NewLine));
        }
        #endregion

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

        #region Public Complaint Reporting Methods
        public static bool GetDefaultQuiet(
            bool @default
            )
        {
            //
            // HOOK: Allow the test suite (and others components) to override
            //       the quietness setting during interpreter creation and to
            //       be able to specify the default fallback value.
            //
            if (CommonOps.Environment.GetVariable(
                    EnvVars.DefaultQuiet) != null)
            {
                return true;
            }

            return @default;
        }

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

        public static bool GetDefaultTraceStack(
            bool @default
            )
        {
            //
            // HOOK: Allow the test suite (and others components) to override
            //       the stack trace setting during interpreter creation and
            //       to be able to specify the default fallback value.
            //
            if (CommonOps.Environment.GetVariable(
                    EnvVars.DefaultTraceStack) != null)
            {
                return true;
            }

            return @default;
        }

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

        [MethodImpl(MethodImplOptions.NoInlining)]
        public static void Complain(
            ReturnCode code,
            Result result
            )
        {
            Complain(Interpreter.GetActive(), code, result);
        }

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

        [MethodImpl(MethodImplOptions.NoInlining)]
        public static void Complain(
            Interpreter interpreter,
            ReturnCode code,
            Result result
            )
        {
            long id = GetComplaintId();

            bool stack = SafeGetTraceStack(interpreter,
                GetDefaultTraceStack(Interpreter.DefaultTraceStack));

            string stackTrace = stack ? GetStackTraceString() : null;

            bool quiet = SafeGetQuiet(interpreter,
                GetDefaultQuiet(Interpreter.DefaultQuiet));

            Complain(
                interpreter, SafeGetDebugTextWriter(interpreter),
                SafeGetHost(interpreter), id, code, result,
                stackTrace, quiet);
        }
        #endregion

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

        #region Private Complaint Reporting Methods
        [MethodImpl(MethodImplOptions.NoInlining)]
        private static void Complain(
            Interpreter interpreter,
            TextWriter textWriter,
            IDebugHost debugHost,
            long id,
            ReturnCode code,
            Result result,
            string stackTrace,
            bool quiet // NOTE: Inhibit use of IDebugHost and the Console?
            )
        {
            int levels = Interlocked.Increment(ref complainLevels);

            try
            {
                if (levels == 1)
                {
                    string formatted = FormatOps.Complaint(
                        id, code, result, stackTrace);

                    SafeSetComplaint(interpreter, formatted);
                    WriteWithoutFail(formatted);

                    if (!quiet)
                    {
                        if (textWriter != null)
                        {
                            try
                            {
                                textWriter.WriteLine(formatted); /* throw */
                                textWriter.Flush(); /* throw */
                            }
                            catch (Exception e)
                            {
                                TextWriteException(id, e);
                            }
                        }

                    retry:

                        if (debugHost != null)
                        {
                            //
                            // BUGFIX: The host may have been disposed at this
                            //         point and we do NOT want to throw an
                            //         exception; therefore, wrap the host
                            //         access in a try block.  If the host does
                            //         throw an exception for any reason, we
                            //         will simply null out the host and retry
                            //         using our default handling.
                            //
                            try
                            {
                                if (FlagOps.HasFlags(debugHost.GetFlags(),
                                        HostFlags.Complain, true))
                                {
                                    debugHost.WriteErrorLine(
                                        formatted); /* throw */
                                }
                            }
                            catch (Exception e)
                            {
                                HostWriteException(id, e);

                                debugHost = null;

                                goto retry;
                            }
                        }
#if WINFORMS
                        else
                        {
                            WindowOps.Complain(formatted);
                        }
#else
                        else
                        {
                            try
                            {
                                TextWriter localTextWriter = Console.Error;

                                if (localTextWriter == null)
                                    localTextWriter = Console.Out;

                                if (localTextWriter != null)
                                {
                                    localTextWriter.WriteLine(
                                        formatted); /* throw */

                                    localTextWriter.Flush(); /* throw */
                                }
                            }
                            catch (Exception e)
                            {
                                TextWriteException(id, e);
                            }
                        }
#endif
                    }
                }
                else if (IsAttached())
                {
                    //
                    // NOTE: This method has been called recursively.  That
                    //       is not a good sign.  Allow the attached debugger
                    //       to see this.
                    //
                    Break();
                }
            }
            finally
            {
                Interlocked.Decrement(ref complainLevels);
            }
        }
        #endregion

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

        #region Public Host Output Methods
        public static void Write(
            Interpreter interpreter,
            string value,
            bool force
            )
        {
            Write(SafeGetDebugTextWriter(interpreter),
                SafeGetHost(interpreter), value, force);
        }
        #endregion

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

        #region Private Host Output Methods
        private static void Write(
            TextWriter textWriter,
            IDebugHost debugHost,
            string value,
            bool force
            )
        {
#if DEBUG
            if (textWriter != null)
#else
            if (force && (textWriter != null))
#endif
            {
                try
                {
                    textWriter.WriteLine(value); /* throw */
                    textWriter.Flush(); /* throw */
                }
                catch (Exception e)
                {
                    TextWriteException(0, e);
                }
            }

#if DEBUG
            if (debugHost != null)
#else
            if (force && (debugHost != null))
#endif
            {
                //
                // BUGFIX: The host may have been disposed at this point and we
                //         do NOT want to throw an exception; therefore, wrap
                //         the host access in a try block.
                //
                try
                {
                    if (FlagOps.HasFlags(debugHost.GetFlags(),
                            HostFlags.Debug, true))
                    {
                        debugHost.WriteDebugLine(value);
                    }
                }
                catch (Exception e)
                {
                    HostWriteException(0, e);
                }
            }
        }
        #endregion

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

        #region Public Debug "Break" Methods
        [MethodImpl(MethodImplOptions.NoInlining)]
        public static void Break(
            Interpreter interpreter,
            MethodBase skipMethod,
            bool force
            )
        {
            Break(SafeGetDebugTextWriter(interpreter),
                SafeGetHost(interpreter), skipMethod, force);
        }

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

        [MethodImpl(MethodImplOptions.NoInlining)]
        public static void Break(
            TextWriter textWriter,
            IDebugHost debugHost,
            MethodBase skipMethod,
            bool force
            )
        {
            Result result = FormatOps.BreakOrFail(
                GetMethodName(skipMethod), "debug break invoked");

            Complain(
                null, textWriter, debugHost, GetComplaintId(),
                ReturnCode.Error, result, GetStackTraceString(),
                false);

#if !DEBUG
            if (force)
#endif
                Break();
        }
        #endregion

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

        #region Public Debug "Fail" Methods
        #region Dead Code
#if DEAD_CODE
        [MethodImpl(MethodImplOptions.NoInlining)]
        public static void Fail(
            Interpreter interpreter,
            MethodBase skipMethod,
            string message,
            string detailMessage,
            bool force
            )
        {
            Fail(SafeGetDebugTextWriter(interpreter),
                SafeGetHost(interpreter), skipMethod,
                message, detailMessage, force);
        }
#endif
        #endregion

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

        [MethodImpl(MethodImplOptions.NoInlining)]
        public static void Fail(
            TextWriter textWriter,
            IDebugHost debugHost,
            MethodBase skipMethod,
            string message,
            string detailMessage,
            bool force
            )
        {
            Result result = FormatOps.BreakOrFail(
                GetMethodName(skipMethod), "debug fail invoked",
                message, detailMessage);

            Complain(
                null, textWriter, debugHost, GetComplaintId(),
                ReturnCode.Error, result, GetStackTraceString(),
                false);

#if !DEBUG
            if (force)
#endif
                Fail(message, detailMessage);
        }
        #endregion

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

        #region Trace Listener Handling Methods
        public static TraceListener NewDefaultTraceListener(
            bool console
            )
        {
#if CONSOLE
            return console ? (TraceListener)
                new ConsoleTraceListener() :
                new DefaultTraceListener();
#else
            return new DefaultTraceListener();
#endif
        }

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

        public static ReturnCode EnsureTraceListener(
            TraceListenerCollection listeners,
            TraceListener listener,
            ref Result error
            )
        {
            if (listeners != null)
            {
                if (listener != null)
                {
                    Type type = listener.GetType();

                    foreach (TraceListener localListener in listeners)
                    {
                        if ((localListener != null) && Object.ReferenceEquals(
                                localListener.GetType(), type))
                        {
                            //
                            // NOTE: We succeeded.  At least one listener of
                            //       this type is already present in the list.
                            //
                            return ReturnCode.Ok;
                        }
                    }

                    //
                    // NOTE: No listeners of this type are present in the
                    //       list, add one now (i.e. the one provided by
                    //       the caller).
                    //
                    listeners.Add(listener);

                    //
                    // NOTE: We succeeded (the listener has been added).
                    //
                    return ReturnCode.Ok;
                }
                else
                {
                    error = "invalid trace listener";
                }
            }
            else
            {
                error = "invalid trace listener collection";
            }

            return ReturnCode.Error;
        }

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

        public static ReturnCode ClearTraceListeners(
            bool debug,
            bool console,
            bool verbose
            )
        {
            Result error = null;

            return ClearTraceListeners(debug, console, verbose, ref error);
        }

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

        private static ReturnCode ClearTraceListeners(
            bool debug,
            bool console,
            bool verbose,
            ref Result error
            )
        {
            TraceListenerCollection listeners = debug ?
                GetDebugListeners() : GetTraceListeners();

            return ClearTraceListeners(listeners, console, verbose, ref error);
        }

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

        private static ReturnCode ClearTraceListeners(
            TraceListenerCollection listeners,
            bool console,
            bool verbose,
            ref Result error
            )
        {
            if (listeners != null)
            {
                listeners.Clear();

#if CONSOLE
                if (console && verbose)
                    Console.WriteLine(_Constants.Prompt.NoTrace);
#endif

                return ReturnCode.Ok;
            }
            else
            {
                error = "invalid trace listener collection";
            }

            return ReturnCode.Error;
        }

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

        private static ReturnCode RemoveTraceListener(
            TraceListenerCollection listeners,
            TraceListener listener,
            ref Result error
            )
        {
            if (listeners != null)
            {
                if (listener != null)
                {
                    listeners.Remove(listener);

                    //
                    // NOTE: We succeeded (the listener has been removed)?
                    //
                    return ReturnCode.Ok;
                }
                else
                {
                    error = "invalid trace listener";
                }
            }
            else
            {
                error = "invalid trace listener collection";
            }

            return ReturnCode.Error;
        }

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

        public static ReturnCode AddTraceListener(
            TraceListener listener,
            bool debug
            )
        {
            Result error = null;

            return AddTraceListener(listener, debug, ref error);
        }

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

        private static ReturnCode AddTraceListener(
            TraceListener listener,
            bool debug,
            ref Result error
            )
        {
            TraceListenerCollection listeners = debug ?
                GetDebugListeners() : GetTraceListeners();

            return EnsureTraceListener(listeners, listener, ref error);
        }

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

        public static ReturnCode RemoveTraceListener(
            TraceListener listener,
            bool debug
            )
        {
            Result error = null;

            return RemoveTraceListener(listener, debug, ref error);
        }

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

        private static ReturnCode RemoveTraceListener(
            TraceListener listener,
            bool debug,
            ref Result error
            )
        {
            TraceListenerCollection listeners = debug ?
                GetDebugListeners() : GetTraceListeners();

            return RemoveTraceListener(listeners, listener, ref error);
        }

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

        public static ReturnCode SetupTraceListeners(
            bool trace,
            bool debug,
            bool console,
            bool verbose
            )
        {
            Result error = null;

            return SetupTraceListeners(
                trace, debug, console, verbose, ref error);
        }

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

        private static ReturnCode SetupTraceListeners(
            bool trace,
            bool debug,
            bool console,
            bool verbose,
            ref Result error
            )
        {
            try
            {
                int count = (trace ? 1 : 0) + (debug ? 1 : 0);
                TraceListener listener = (trace || debug) ?
                    NewDefaultTraceListener(console) : null;

                //
                // NOTE: Do they want to add a normal trace listener?
                //
                if (trace)
                {
                    Result localError = null;

                    if (AddTraceListener(listener, false,
                            ref localError) == ReturnCode.Ok)
                    {
                        count--;

#if CONSOLE
                        if (console && verbose)
                            Console.WriteLine(_Constants.Prompt.Trace);
#endif
                    }
#if CONSOLE
                    else
                    {
                        //
                        // TODO: Can this actually happen?
                        //
                        if (console && verbose)
                            Console.WriteLine(String.Format(
                                _Constants.Prompt.TraceError, localError));
                    }
#endif
                }

                //
                // NOTE: Do they want to add a debug trace listener as well?
                //
                if (debug)
                {
                    Result localError = null;

                    if (AddTraceListener(listener, true,
                            ref localError) == ReturnCode.Ok)
                    {
                        count--;

#if CONSOLE
                        if (console && verbose)
                            Console.WriteLine(_Constants.Prompt.DebugTrace);
#endif
                    }
#if CONSOLE
                    else
                    {
                        //
                        // TODO: Can this actually happen?
                        //
                        if (console && verbose)
                            Console.WriteLine(String.Format(
                                _Constants.Prompt.DebugTraceError, localError));
                    }
#endif
                }

                if (count == 0)
                    return ReturnCode.Ok;
                else
                    error = "one or more trace listeners could not be added";
            }
            catch (Exception e)
            {
                error = e;
            }

            return ReturnCode.Error;
        }
        #endregion

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

        #region Framework Wrapper Methods
        public static void Break()
        {
            System.Diagnostics.Debugger.Break();
        }

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

        private static void Fail(string message, string detailMessage)
        {
            Debug.Fail(message, detailMessage);
        }

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

        public static bool IsAttached()
        {
            return System.Diagnostics.Debugger.IsAttached;
        }

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

        public static TraceListenerCollection GetDebugListeners()
        {
            return Debug.Listeners;
        }

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

        public static TraceListenerCollection GetTraceListeners()
        {
            return Trace.Listeners;
        }

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

        public static void Log(
            string message
            )
        {
            System.Diagnostics.Debugger.Log(0, DefaultCategory, message);
        }

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

        #region Dead Code
#if DEAD_CODE
        private static void Log(
            int level,
            string message
            )
        {
            System.Diagnostics.Debugger.Log(level, DefaultCategory, message);
        }
#endif
        #endregion

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

        public static void Log(
            int level,
            string category,
            string message
            )
        {
            System.Diagnostics.Debugger.Log(level, category, message);
        }

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

        public static void DebugWrite(
            object value
            )
        {
            Debug.Write(value);
        }

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

        public static void DebugWrite(
            string message
            )
        {
            Debug.Write(message);
        }

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

        public static void DebugWrite(
            string message,
            string category
            )
        {
            if (category != null)
                Debug.Write(message, category);
            else
                Debug.Write(message);
        }

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

        public static void DebugWriteLine(
            object value
            )
        {
            Debug.WriteLine(value);
        }

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

        public static void DebugWriteLine(
            string message
            )
        {
            Debug.WriteLine(message);
        }

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

        private static void DebugWriteLine(
            string message,
            string category
            )
        {
            if (category != null)
                Debug.WriteLine(message, category);
            else
                Debug.WriteLine(message);
        }

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

        #region Dead Code
#if DEAD_CODE
        private static void DebugFlush()
        {
            Debug.Flush();
        }
#endif
        #endregion

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

        public static void TraceWrite(
            object value
            )
        {
            Trace.Write(value);
        }

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

        public static void TraceWrite(
            string message
            )
        {
            Trace.Write(message);
        }

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

        public static void TraceWrite(
            string message,
            string category
            )
        {
            if (category != null)
                Trace.Write(message, category);
            else
                Trace.Write(message);
        }

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

        public static void TraceWriteLine(
            object value
            )
        {
            Trace.WriteLine(value);
        }

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

        public static void TraceWriteLine(
            string message
            )
        {
            Trace.WriteLine(message);
        }

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

        public static void TraceWriteLine(
            string message,
            string category
            )
        {
            if (category != null)
                Trace.WriteLine(message, category);
            else
                Trace.WriteLine(message);
        }

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

        public static void TraceFlush()
        {
            Trace.Flush();
        }
        #endregion

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

        #region Miscellaneous Debugging Methods
        public static void DumpAppDomain(
            AppDomain appDomain
            )
        {
            if (appDomain != null)
            {
                TraceWrite(String.Format(
                    "{0}: DumpAppDomain: Id = {1}, FriendlyName = {2}, " +
                    "BaseDirectory = {3}, RelativeSearchPath = {4}, " +
                    "DynamicDirectory = {5}, ShadowCopyFiles = {6}{7}",
                    GlobalState.GetCurrentThreadId(), appDomain.Id,
                    FormatOps.WrapOrNull(appDomain.FriendlyName),
                    FormatOps.WrapOrNull(appDomain.BaseDirectory),
                    FormatOps.WrapOrNull(appDomain.RelativeSearchPath),
                    FormatOps.WrapOrNull(appDomain.DynamicDirectory),
                    appDomain.ShadowCopyFiles, Environment.NewLine),
                    typeof(DebugOps).Name);

                foreach (Assembly assembly in appDomain.GetAssemblies())
                {
                    string name = null;
                    string location = null;

                    if (assembly != null)
                    {
                        AssemblyName assemblyName = assembly.GetName();

                        if (assemblyName != null)
                            name = assemblyName.ToString();

                        location = assembly.Location;
                    }

                    TraceWrite(String.Format(
                        "{0}: DumpAppDomain: assemblyName = {1}, " +
                        "location = {2}{3}", GlobalState.GetCurrentThreadId(),
                        FormatOps.WrapOrNull(name), FormatOps.WrapOrNull(
                        location), Environment.NewLine),
                        typeof(DebugOps).Name);
                }
            }
            else
            {
                TraceWrite(String.Format(
                    "{0}: DumpAppDomain: invalid application domain{1}",
                    GlobalState.GetCurrentThreadId(), Environment.NewLine),
                    typeof(DebugOps).Name);
            }
        }
        #endregion
    }
}
