/*
 * EventOps.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 Eagle._Attributes;
using Eagle._Components.Public;
using Eagle._Constants;
using Eagle._Interfaces.Public;

namespace Eagle._Components.Private
{
    [ObjectId("214d2a13-4973-41cd-a765-3f94b3c514ca")]
    internal static class EventOps
    {
        #region Private Constants
        #region Wait Handling
        private static readonly int WaitDivisor = 2;
        private static readonly int WaitMaximumSleepTime = 50; /* in milliseconds */

        private static readonly int WaitSlopDivisor = 40;
        private static readonly int WaitSlopMinimumTime = 25000;  /* in microseconds */
        #endregion
        #endregion

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

        #region Event Manager Support Methods
        public static bool ManagerIsOk(
            IEventManager eventManager
            ) /* THREAD-SAFE */
        {
            if (eventManager == null)
                return false;

            if (eventManager.Disposed)
                return false;

            return true;
        }

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

        public static void Sleep(
            Interpreter interpreter,
            bool minimum
            ) /* THREAD-SAFE */
        {
            if (interpreter != null)
            {
                IEventManager eventManager = interpreter.EventManager;

                if (ManagerIsOk(eventManager))
                    eventManager.Sleep(minimum);
            }
        }

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

        public static ReturnCode ProcessEvents(
            Interpreter interpreter,
            EventFlags eventFlags,
            EventPriority priority,
            int limit,
            bool stopOnError,
            bool errorOnEmpty,
            ref Result result
            )
        {
            if (interpreter != null)
            {
                IEventManager eventManager = interpreter.EventManager;

                if (ManagerIsOk(eventManager))
                {
                    return eventManager.ProcessEvents(
                        eventFlags, priority, limit, stopOnError, errorOnEmpty,
                        ref result);
                }
                else
                {
                    result = "event manager not available";
                }
            }
            else
            {
                result = "invalid interpreter";
            }

            return ReturnCode.Error;
        }

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

        public static ReturnCode DoOneEvent(
            Interpreter interpreter,
            EventFlags eventFlags,
            EventPriority priority,
            int limit, /* NOTE: Pass zero for ALL. */
            bool stopOnError,
            bool errorOnEmpty,
            bool userInterface,
            ref Result result
            )
        {
            if (interpreter != null)
            {
                //
                // BUGFIX: Wrap in try/catch in case the interpreter is
                //         disposed (from WaitVariable).
                //
                try
                {
                    IEventManager eventManager = interpreter.EventManager;

                    if (ManagerIsOk(eventManager))
                    {
                        return eventManager.DoOneEvent(
                            eventFlags, priority, limit, stopOnError,
                            errorOnEmpty, userInterface, ref result);
                    }
                    else
                    {
                        result = "event manager not available";
                    }
                }
                catch (Exception e)
                {
                    result = e;
                }
            }
            else
            {
                result = "invalid interpreter";
            }

            return ReturnCode.Error;
        }

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

        public static ReturnCode Wait(
            Interpreter interpreter,
            long microseconds,
            bool timeout,
            bool strict,
            ref Result error
            ) /* THREAD-SAFE */
        {
            ReturnCode code = ReturnCode.Ok;

            if (interpreter != null)
            {
                int waitCount;

                if ((waitCount = interpreter.EnterWait()) > 0)
                {
                    if (microseconds == 0)
                    {
#if NATIVE && WINDOWS && WINFORMS
                        //
                        // NOTE: If necessary, process all Windows messages
                        //       from the queue.
                        //
                        if (!strict)
                        {
                            code = WindowOps.ProcessEvents(
                                interpreter, ref error);
                        }

                        if (code == ReturnCode.Ok)
#endif
                        {
                            //
                            // NOTE: Yield to other running threads.  This
                            //       also gives them an opportunity to cancel
                            //       the script in progress on this thread.
                            //
                            HostOps.Yield();
                        }
                    }
                    else
                    {
                        //
                        // NOTE: Keep track of how many iterations through
                        //       the loop we take.
                        //
                        int iterations = 0;

                        //
                        // HACK: Account for our processing overhead; use half
                        //       of the requested delay.
                        //
                        int milliseconds = ConversionOps.ToInt(
                            PerformanceOps.GetMilliseconds(microseconds) /
                            WaitDivisor);

                        if (milliseconds < 0)
                            milliseconds = 0;

                        if (milliseconds > WaitMaximumSleepTime)
                            milliseconds = WaitMaximumSleepTime;

                        //
                        // NOTE: For more precise timing, use the high-resolution
                        //       CPU performance counter.
                        //
                        long startCount = PerformanceOps.GetCount();

                        //
                        // BUGFIX: Make sure the slop time does not exceed the
                        //         actual wait.
                        //
                        long slopMicroseconds = Math.Min(
                            microseconds / WaitSlopDivisor, WaitSlopMinimumTime);

                        //
                        // NOTE: Delay for approximately the specified number of
                        //       microseconds, optionally timing out if we cannot
                        //       obtain the interpreter lock before the time period
                        //       elapses.
                        //
                        while (((code = Interpreter.EventReady(interpreter,
                                timeout ? milliseconds : _Timeout.Infinite,
                                ref error)) == ReturnCode.Ok) &&
                            !PerformanceOps.HasElapsed(startCount,
                                microseconds, slopMicroseconds))
                        {
#if NATIVE && WINDOWS && WINFORMS
                            if (!strict)
                            {
                                code = WindowOps.ProcessEvents(interpreter, ref error);

                                if (code != ReturnCode.Ok)
                                    break;
                            }
#endif

                            HostOps.Sleep(interpreter, milliseconds); iterations++;
                        }

                        long stopCount = PerformanceOps.GetCount();

                        double elapsedMicroseconds = PerformanceOps.GetMicroseconds(
                            startCount, stopCount, 1);

                        TraceOps.DebugTrace(String.Format(
                            "{0}: Wait: code = {1}, iterations = {2}, microseconds = {3}, " +
                            "elapsedMicroseconds = {4}, sleepMilliseconds = {5}, " +
                            "slopMicroseconds = {6}, differenceMicroseconds = {7}, " +
                            "waitCount = {8}, error = {9}{10}", GlobalState.GetCurrentThreadId(),
                            code, iterations, microseconds, elapsedMicroseconds, milliseconds,
                            slopMicroseconds, elapsedMicroseconds - (double)microseconds,
                            waitCount, FormatOps.WrapOrNull(true, true, error), Environment.NewLine),
                            typeof(EventOps).Name);
                    }

                    /* IGNORED */
                    interpreter.ExitWait();
                }
                else
                {
                    error = "wait subsystem locked";
                    code = ReturnCode.Error;
                }
            }
            else
            {
                error = "invalid interpreter";
                code = ReturnCode.Error;
            }

            return code;
        }
        #endregion
    }
}
