using System;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Security;
using System.Windows.Forms;

namespace ComponentFramework
{
    /// <summary>
    /// Handles the message-based looping mechanisms.
    /// See : http://blogs.msdn.com/tmiller/archive/2005/05/05/415008.aspx
    /// and http://pinvoke.net/default.aspx/user32/PeekMessage.html
    /// </summary>
    class Looping
    {
        /// <summary>
        /// The Windows API Message structure.
        /// </summary>
        [StructLayout(LayoutKind.Sequential)]
        struct Message
        {
            public IntPtr handle;
            public uint msg;
            public IntPtr wParam, lParam;
            public uint time;
            public Point p;
        }

        /// <summary>
        /// The PeekMessage PInvoke.
        /// </summary>
        [SuppressUnmanagedCodeSecurity]
        [return: MarshalAs(UnmanagedType.Bool)]
        [DllImport("User32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        static extern bool PeekMessage(out Message message, IntPtr handle, uint filterMin, uint filterMax, uint flags);

        /// <summary>
        /// A direct reference to the core.
        /// </summary>
        readonly Core core;

        /// <summary>
        /// Whether looping is currently paused.
        /// </summary>
        bool paused;

        /// <summary>
        /// Parameterized constructor. Hooks itself onto the <see cref="Application.Idle"/> event.
        /// </summary>
        /// <param name="core"></param>
        public Looping(Core core)
        {
            this.core = core;
            Application.Idle += Mainloop;
        }

        /// <summary>
        /// The main loop handler.
        /// </summary>
        /// <param name="sender">Ignored</param>
        /// <param name="e">Ignored</param>
        void Mainloop(object sender, EventArgs e)
        {
            while (!paused && AppStillIdle)
            {
                core.UpdateInternal();
                core.Draw();
            }
        }

        /// <summary>
        /// Tests if the application can continue looping.
        /// </summary>
        static bool AppStillIdle
        {
            get
            {
                Message msg;
                return !PeekMessage(out msg, IntPtr.Zero, 0, 0, 0);
            }
        }

        /// <summary>
        /// Stops looping and exits the application.
        /// </summary>
        public void StopLoop()
        {
            Application.Idle -= Mainloop;
            Application.Exit();
        }

        /// <summary>
        /// Pauses looping until the <see cref="ResumeLoop"/> method is called.
        /// </summary>
        public void PauseLoop()
        {
            if (!paused)
            {
                Application.Idle -= Mainloop;
                paused = true;
            }
        }

        /// <summary>
        /// Resumes looping from a <see cref="PauseLoop"/> call.
        /// </summary>
        public void ResumeLoop()
        {
            if (paused)
            {
                Application.Idle += Mainloop;
                paused = false;
            }
        }
    }
}
