/*
 * DebuggerOps.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: $
 */

#if DEBUGGER
using System;
#endif

using Eagle._Attributes;
using Eagle._Components.Public;

#if DEBUGGER
using Eagle._Components.Public.Delegates;
using Eagle._Containers.Public;
using Eagle._Interfaces.Private;
using Eagle._Interfaces.Public;
#endif

namespace Eagle._Components.Private
{
    [ObjectId("438a61ad-907d-4089-a80c-c6d5e7edac05")]
    internal static class DebuggerOps
    {
        #region Private Constants
#if DEBUGGER
        private static readonly EngineFlags BreakpointEngineFlags =
            EngineFlags.None;

        private static readonly EngineFlags WatchpointEngineFlags =
            EngineFlags.None;

        private static readonly EngineFlags InteractiveEngineFlags =
            EngineFlags.NoBreakpoint | EngineFlags.Interactive;
#endif
        #endregion

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

        #region Debugger Interpreter Support Methods
#if DEBUGGER
        public static Interpreter CreateInterpreter(
            string culture,
            CreateFlags createFlags,
            AppDomain appDomain,
            IHost host
            )
        {
            Result result = null;

            Interpreter interpreter = CreateInterpreter(
                culture, createFlags, appDomain, host, ref result);

            if (interpreter == null)
                DebugOps.Complain(interpreter, ReturnCode.Error, result);

            return interpreter;
        }

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

        public static Interpreter CreateInterpreter(
            string culture,
            CreateFlags createFlags,
            AppDomain appDomain,
            IHost host,
            ref Result result
            )
        {
            //
            // NOTE: First, mask off flags that we know to be invalid for
            //       debugger interpreters.  Next, add a flag to prevent a
            //       default interpreter host from being created.  Finally,
            //       create an isolated debugging interpreter with the right
            //       set of options.  This is actually a very delicate
            //       operation because we need to clone the provided host into
            //       the new interpreter; however, we cannot do that until it
            //       is created.  The solution is to create the interpreter
            //       without any host whatsoever and then use it to create a
            //       clone of the provided host.  The newly cloned host can
            //       then be assigned to the newly created interpreter.
            //
            createFlags &= ~CreateFlags.NonDebuggerUse;
            createFlags |= CreateFlags.NoHost;

            Interpreter interpreter = Interpreter.Create(
                culture, createFlags, appDomain, ref result);

            if (interpreter != null)
            {
                interpreter.Host = (host != null) ?
                    host.Clone(interpreter) : null;
            }

            return interpreter;
        }
#endif
        #endregion

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

        #region Debugger Watchpoint Support Methods
#if DEBUGGER
        public static ReturnCode Watchpoint(
            IDebugger debugger,
            Interpreter interpreter,
            ReturnCode code,
            BreakpointType breakpointType,
            string breakpointName,
            IToken token,
            ITraceInfo traceInfo,
            EngineFlags engineFlags,
            SubstitutionFlags substitutionFlags,
            EventFlags eventFlags,
            ExpressionFlags expressionFlags,
            HeaderFlags headerFlags,
            ref Result result
            )
        {
            bool exit = false;

            return Watchpoint(
                debugger, interpreter, code, breakpointType, breakpointName,
                token, traceInfo, engineFlags, substitutionFlags, eventFlags,
                expressionFlags, headerFlags, ref exit, ref result);
        }

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

        public static ReturnCode Watchpoint(
            IDebugger debugger,
            Interpreter interpreter,
            ReturnCode code,
            BreakpointType breakpointType,
            string breakpointName,
            IToken token,
            ITraceInfo traceInfo,
            EngineFlags engineFlags,
            SubstitutionFlags substitutionFlags,
            EventFlags eventFlags,
            ExpressionFlags expressionFlags,
            HeaderFlags headerFlags,
            ref bool exit,
            ref Result result
            )
        {
            return Breakpoint(
                debugger, interpreter, code, breakpointType, breakpointName,
                token, traceInfo, engineFlags | WatchpointEngineFlags,
                substitutionFlags, eventFlags, expressionFlags, headerFlags,
                null, null, ref exit, ref result);
        }
#endif
        #endregion

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

        #region Debugger Breakpoint Support Methods
#if DEBUGGER
        public static ReturnCode Breakpoint(
            IDebugger debugger,
            Interpreter interpreter,
            ReturnCode code,
            BreakpointType breakpointType,
            string breakpointName,
            IToken token,
            ITraceInfo traceInfo,
            EngineFlags engineFlags,
            SubstitutionFlags substitutionFlags,
            EventFlags eventFlags,
            ExpressionFlags expressionFlags,
            HeaderFlags headerFlags,
            IClientData clientData,
            ArgumentList arguments,
            ref Result result
            )
        {
            bool exit = false;

            return Breakpoint(
                debugger, interpreter, code, breakpointType, breakpointName,
                token, traceInfo, engineFlags, substitutionFlags, eventFlags,
                expressionFlags, headerFlags, clientData, arguments, ref exit,
                ref result);
        }

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

        public static ReturnCode Breakpoint(
            IDebugger debugger,
            Interpreter interpreter,
            ReturnCode code,
            BreakpointType breakpointType,
            string breakpointName,
            IToken token,
            ITraceInfo traceInfo,
            EngineFlags engineFlags,
            SubstitutionFlags substitutionFlags,
            EventFlags eventFlags,
            ExpressionFlags expressionFlags,
            HeaderFlags headerFlags,
            IClientData clientData,
            ArgumentList arguments,
            ref bool exit,
            ref Result result
            )
        {
            if (interpreter != null)
            {
                if (interpreter.Interactive)
                {
                    InteractiveLoopCallback interactiveLoopCallback =
                        interpreter.InteractiveLoopCallback;

                    if (interactiveLoopCallback != null)
                    {
                        return interactiveLoopCallback(
                            interpreter, true, null, code, breakpointType,
                            breakpointName, token, traceInfo, engineFlags |
                                BreakpointEngineFlags, substitutionFlags,
                            eventFlags, expressionFlags, headerFlags,
                            clientData, arguments, ref exit, ref result);
                    }
                    else
                    {
#if SHELL
                        //
                        // NOTE: This is the only place in this class where the
                        //       InteractiveLoop method may be called.  All
                        //       other methods in the Debugger class and/or any
                        //       external classes that desire the interactive
                        //       debugging functionality should call this
                        //       Debugger.Breakpoint method overload.
                        //
                        return Interpreter.InteractiveLoop(
                            interpreter, true, null, code, breakpointType,
                            breakpointName, token, traceInfo, engineFlags |
                                BreakpointEngineFlags, substitutionFlags,
                            eventFlags, expressionFlags, headerFlags,
                            clientData, arguments, ref exit, ref result);
#else
                        result = "not implemented";
#endif
                    }
                }
                else
                {
                    result = "cannot break into interactive loop";
                }
            }
            else
            {
                result = "invalid interpreter";
            }

            return ReturnCode.Error;
        }
#endif
        #endregion

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

        #region Debugger General Support Methods
        public static bool GetBreakOnCancel(
            Interpreter interpreter,
            bool ignoreEnabled
            )
        {
#if DEBUGGER
            if (interpreter != null)
            {
                lock (interpreter.SyncRoot) /* TRANSACTIONAL */
                {
                    IDebugger debugger = interpreter.Debugger;

                    if ((debugger != null) &&
                        (ignoreEnabled || debugger.Enabled))
                    {
                        return debugger.BreakOnCancel;
                    }
                }
            }
#endif

            return false;
        }

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

        public static string GetCommand(
            Interpreter interpreter,
            bool ignoreEnabled
            )
        {
            string result = null;

#if DEBUGGER
            if (interpreter != null)
            {
                lock (interpreter.SyncRoot) /* TRANSACTIONAL */
                {
                    IDebugger debugger = interpreter.Debugger;

                    if ((debugger != null) &&
                        (ignoreEnabled || debugger.Enabled))
                    {
                        //
                        // NOTE: Enforce "one-time" semantics.
                        //
                        result = debugger.Command;
                        debugger.Command = null;
                    }
                }
            }
#endif

            return result;
        }

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

        public static HeaderFlags GetHeaderFlags(
            HeaderFlags headerFlags,
            bool debug,
            bool show,
            bool empty,
            bool @default
            )
        {
            //
            // NOTE: If we are in debug mode and no header display flags have
            //       been explicitly set for the interpreter, initialize them
            //       to the default value.
            //
            if (@default && FlagOps.HasFlags(
                    headerFlags, HeaderFlags.Invalid, true))
            {
                //
                // NOTE: Remove the "these flags have not been setup before"
                //       indicator flag.
                //
                headerFlags &= ~HeaderFlags.Invalid;

                //
                // NOTE: Add the default header flags.
                //
                headerFlags |= HeaderFlags.Default;
            }

            //
            // NOTE: Only modify (set or unset) the active debugger flag if we
            //       have been told to do so; otherwise, the active debugger
            //       flag may have been manually changed and should be left
            //       alone.
            //
            if (show)
            {
                //
                // NOTE: Is there an active debugger?
                //
                if (debug)
                {
                    //
                    // NOTE: Set the active debugger flag.
                    //
                    headerFlags |= HeaderFlags.Debug;
                }
                else
                {
                    //
                    // NOTE: Unset the active debugger flag.
                    //
                    headerFlags &= ~HeaderFlags.Debug;
                }
            }

            //
            // NOTE: Show empty content?
            //
            if (empty)
                headerFlags |= HeaderFlags.EmptyContent;

            return headerFlags;
        }

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

        public static EngineFlags GetEngineFlags(
            bool debug
            )
        {
#if DEBUGGER
            if (debug)
                return InteractiveEngineFlags;
#endif

            return EngineFlags.Interactive;
        }

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

#if DEBUGGER
        public static bool CanHitBreakpoints(
            EngineFlags engineFlags,
            Interpreter interpreter
            )
        {
            return !Engine.HasNoBreakpoint(engineFlags) &&
                (interpreter != null) && !interpreter.Disposed &&
                interpreter.CanHitBreakpoints();
        }

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

        public static IDebugger Create(
            bool isolated,
            string culture,
            CreateFlags createFlags,
            AppDomain appDomain,
            IHost host
            )
        {
            return new Debugger(
                isolated, culture, createFlags, appDomain, host);
        }
#endif

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

        public static bool SetResult(
            Interpreter interpreter,
            ReturnCode code,
            Result result,
            bool ignoreEnabled
            )
        {
#if DEBUGGER
            if (interpreter != null)
            {
                lock (interpreter.InternalSyncRoot) /* TRANSACTIONAL */
                {
                    if (!interpreter.Disposed)
                    {
                        IDebugger debugger = interpreter.Debugger;

                        if ((debugger != null) && !debugger.Disposed &&
                            (ignoreEnabled || debugger.Enabled))
                        {
                            //
                            // NOTE: Enforce "copy" semantics.
                            //
                            debugger.Result = Result.Copy(
                                code, result, true); /* COPY */

                            return true;
                        }
                    }
                }
            }
#endif

            return false;
        }
        #endregion
    }
}
