using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Runtime.InteropServices;

// ensure you have read the Readme and license.txt file!

/* License:
I am releasing this app with source code under the GNU GPL (GNU General Public License) with the following terms:
- It is NOT to be used in any form for commercial purposes!
- Code may not be used in other apps for any purpose.
- Changes may be made to the code provided ALL changes are clearly marked with #region tags and \\\ documentation tags. The documentation tags
  must include:
  - the author of the change(s)
  - why the change(s) was/were made
  - the complete source code is emailed to TheDarkWraith@hotmail.com
- Additions may be added to the code provided that ALL additions are clearly marked with #region tags and \\\ documentation tags. The documentation tags
  must include:
  - the author of the addition(s)
  - why the addition(s) was/were made
  - the complete source code is emailed to TheDarkWraith@hotmail.com
- The assembly name will remain as Weber-Marelli 1_6M_ECU_Tools
- The default namespace will remain as TheDarkWraith.WM16MEV
- The icon for the main form (WM16MEV.cs) will remain as MrFX_Racerboy.ico (the current icon)
- Any new forms added will use the same icon (MrFX_Racerboy.ico)
- The target framework will remain .NET framework 2.0
- The title of the main form (WM16MEV.cs) will remain as Weber-Marelli 1.6M ECU Tools by TheDarkWraith vy where y is the current exe version as built
- Individual code files (i.e. SerialComms.cs for one) will not be used outside of the app and may NOT be used in any other app
- The code will remain C# code and WILL NOT be translated to any other computer language
- All new code files and forms will contain a copy of this license in complete in comment tags right after any using statements at start of file
- The status strip (statusStrip1) of the main form (WM16MEV.cs) WILL NOT BE DELETED OR HIDDEN and the right hand side of this status strip WILL contain the tool strip status label
  called tsslLicense WITH the following text: MAY NOT BE USED FOR COMMERCIAL PURPOSES
*/


/// <summary>
/// High precision timer for gaming use
/// </summary>
public class HighPrecisionTimer
{
    /// <summary>
    /// Enumeration for various actions our timer can perform
    /// </summary>
    public enum HPT
    {
        Reset,
        ResetTo,
        Start,
        Stop,
        Pause,
        Unpause,
        Advance,
        GetAbsoluteTime,
        GetApplicationTime,
        GetElapsedTime,
        UsingQueryPerformanceCounter
    };


    #region Timer Internal Stuff
    [System.Security.SuppressUnmanagedCodeSecurity] // We won't use this maliciously
    [DllImport("kernel32")]
    private static extern bool QueryPerformanceFrequency(ref long PerformanceFrequency);
    [System.Security.SuppressUnmanagedCodeSecurity] // We won't use this maliciously
    [DllImport("kernel32")]
    private static extern bool QueryPerformanceCounter(ref long PerformanceCount);
    [System.Security.SuppressUnmanagedCodeSecurity] // We won't use this maliciously
    [DllImport("winmm.dll")]
    public static extern int timeGetTime();
    private bool isTimerInitialized = false;
    private bool m_bUsingQPF = false;
    private bool m_bTimerStopped = true;
    private long m_llQPFTicksPerSec = 0;
    //private long m_llStopTime = 0;
    //private long m_llLastElapsedTime = 0;
    //private long m_llBaseTime = 0;
    //private double m_fLastElapsedTime = 0.0;
    //private double m_fBaseTime = 0.0;
    //private double m_fStopTime = 0.0;

    double time;
    double fElapsedTime;
    long qwTime = 0;

    private bool paused = false;

    // for high precision timer
    private long hpt_pauseTime = 0;
    private long hpt_startTime = 0;
    private long hpt_stopTime = 0;
    private long hpt_elapsedTime = 0;

    // for non-precision timer
    private double pauseTime = 0.0;
    private double startTime = 0.0;
    private double stopTime = 0.0;
    private double elapsedTime = 0.0;
    
    
    public static double resetTime = 0.0;
    #endregion


    /// <summary>
    /// Performs timer opertations. Use the following commands:
    /// 
    ///          HPT.Reset - to reset the timer. If stopped timer will restart.
    ///          HPT.ResetTo - resets the timer to a specified time. Time adjustment placed in static resetTime
    ///          HPT.Start - to start the timer
    ///          HPT.Stop - to stop (or pause) the timer
    ///          HPT.Pause - Pauses the timer
    ///          HPT.UnPause - Unpauses the timer
    ///          HPT.Advance - to advance the timer by 0.1 seconds
    ///          HPT.GetAbsoluteTime - to get the absolute system time (the time that is now)
    ///          HPT.GetApplicationTime - to get the current time (since start time)
    ///          HPT.GetElapsedTime - to get the time that elapsed between TIMER_GETELAPSEDTIME calls (time between last elapsed time call and now)
    ///
    /// </summary>
    public float Timer(HPT command)
    {
        if (!isTimerInitialized)
        {
            isTimerInitialized = true;
            paused = false;

            // Use QueryPerformanceFrequency() to get frequency of timer.  If QPF is
            // not supported, we will timeGetTime() which returns milliseconds.
            long qwTicksPerSec = 0;
            m_bUsingQPF = QueryPerformanceFrequency(ref qwTicksPerSec);
            if (m_bUsingQPF)
                m_llQPFTicksPerSec = qwTicksPerSec;
        }

        if (command == HPT.UsingQueryPerformanceCounter)
        {
            if (m_bUsingQPF) return 1f;
            else return -1f;
        }

        // if using the ultra-high performance timer
        if (m_bUsingQPF)
        {
            // Get either the current time or the stop time, depending
            // on whether we're stopped and what command was sent
            //if (m_llStopTime != 0 && command != HPT.Start && command != HPT.GetAbsoluteTime)
            //    qwTime = m_llStopTime;
            if (hpt_stopTime != 0 && command != HPT.Start && command != HPT.GetAbsoluteTime)
                qwTime = hpt_stopTime;
            else
                QueryPerformanceCounter(ref qwTime);

            // Pause
            if (command == HPT.Pause)
            {
                if (!paused)
                {
                    hpt_pauseTime = qwTime;
                    paused = true;
                    return 1f;
                }
            }

            // Unpause
            if (command == HPT.Unpause)
            {
                // make sure timer isn't already unpaused
                if (this.paused)
                {
                    paused = false;
                    hpt_startTime += qwTime - hpt_pauseTime;
                    hpt_elapsedTime += qwTime - hpt_pauseTime;
                    return 1f;
                }
            }

            // ResetTo
            if (command == HPT.ResetTo)
            {
                hpt_startTime = qwTime - (long)(resetTime * m_llQPFTicksPerSec);
                return 1f;
            }

            // Return the elapsed time
            if (command == HPT.GetElapsedTime)
            {
                //fElapsedTime = (double)(qwTime - m_llLastElapsedTime) / (double)m_llQPFTicksPerSec;
                //m_llLastElapsedTime = qwTime;
                //return (float)fElapsedTime;
                long hpt_elapsed = qwTime - hpt_elapsedTime;
                hpt_elapsedTime = qwTime;
                return (float)(hpt_elapsed / (double)m_llQPFTicksPerSec);

            }

            // Return the current time
            if (command == HPT.GetApplicationTime)
            {
                //double fAppTime = (double)(qwTime - m_llBaseTime) / (double)m_llQPFTicksPerSec;
                //return (float)fAppTime;
                
                // if paused, return time difference from when timer was paused
                if (this.paused)
                    return (float)(hpt_pauseTime - hpt_startTime);
                // if not, then return time normally
                else
                    return (float)((qwTime - hpt_startTime) / (double)m_llQPFTicksPerSec);
            }

            // Reset the timer
            if (command == HPT.Reset)
            {
                //m_llBaseTime = qwTime;
                //m_llLastElapsedTime = qwTime;
                //m_llStopTime = 0;
                //m_bTimerStopped = false;

                hpt_startTime = qwTime;
                hpt_elapsedTime = hpt_startTime;
                hpt_stopTime = 0;
                m_bTimerStopped = false;
                return 1.0f;
            }

            // Start the timer
            if (command == HPT.Start)
            {
                //if (m_bTimerStopped)
                //    m_llBaseTime += qwTime - m_llStopTime;
                //m_llStopTime = 0;
                //m_llLastElapsedTime = qwTime;
                //m_bTimerStopped = false;
                //return 0.0f;

                if (m_bTimerStopped)
                    hpt_startTime += qwTime - hpt_stopTime;
                hpt_stopTime = 0;
                hpt_elapsedTime = qwTime;
                m_bTimerStopped = false;
                return 1f;
            }

            // Stop the timer
            if (command == HPT.Stop)
            {
                //if (!m_bTimerStopped)
                //{
                //    m_llStopTime = qwTime;
                //    m_llLastElapsedTime = qwTime;
                //    //m_bTimerStopped = true;
                //}
                //return 0.0f;

                if (!m_bTimerStopped)
                {
                    hpt_stopTime = qwTime;
                    hpt_elapsedTime = qwTime;
                    m_bTimerStopped = true;
                }
                return 1f;
            }

            // Advance the timer by 1/10th second
            if (command == HPT.Advance)
            {
                //m_llStopTime += m_llQPFTicksPerSec / 10;
                hpt_stopTime += m_llQPFTicksPerSec / 10;
                return 1f;
            }

            if (command == HPT.GetAbsoluteTime)
            {
                time = qwTime / (double)m_llQPFTicksPerSec;
                return (float)time;
            }

            return -1.0f; // Invalid command specified
        }
        
        // the ultra-high performance timer wasn't available so default to less precision one
        else
        {
            // Get the time using timeGetTime()
            // Get either the current time or the stop time, depending
            // on whether we're stopped and what command was sent
            //if (m_fStopTime != 0.0 && command != HPT.Start && command != HPT.GetAbsoluteTime)
            //    time = m_fStopTime;
            if (stopTime != 0.0 && command != HPT.Start && command != HPT.GetAbsoluteTime)
                time = stopTime;
            else
                time = timeGetTime() * 0.001;

            // Pause
            if (command == HPT.Pause)
            {
                if (!paused)
                {
                    pauseTime = time;
                    paused = true;
                    return 1f;
                }
            }

            // Unpause
            if (command == HPT.Unpause)
            {
                // make sure timer isn't already unpaused
                if (this.paused)
                {
                    paused = false;
                    startTime += time - pauseTime;
                    elapsedTime += time - pauseTime;
                    return 1f;
                }
            }

            // ResetTo
            if (command == HPT.ResetTo)
            {
                startTime = time - resetTime;
                return 1f;
            }

            // Return the elapsed time
            if (command == HPT.GetElapsedTime)
            {
                //fElapsedTime = (double)(time - m_fLastElapsedTime);
                //m_fLastElapsedTime = time;
                //return (float)fElapsedTime;
                double elapsed = time - elapsedTime;
                elapsedTime = time;
                return (float)elapsed;
            }

            // Return the current time
            if (command == HPT.GetApplicationTime)
            {
                //return (float)(time - m_fBaseTime);
                // if paused, return time difference from when timer was paused
                if (this.paused)
                    return (float)(pauseTime - startTime);

                // if not, then return time normally
                else
                    return (float)(time - startTime);
            }

            // Reset the timer
            if (command == HPT.Reset)
            {
                //m_fBaseTime = time;
                //m_fLastElapsedTime = time;
                //m_fStopTime = 0;
                //m_bTimerStopped = false;

                startTime = time;
                elapsedTime = time;
                stopTime = 0;
                m_bTimerStopped = false;
                return 1f;
            }

            // Start the timer
            if (command == HPT.Start)
            {
                //if (m_bTimerStopped)
                //    m_fBaseTime += time - m_fStopTime;
                //m_fStopTime = 0.0f;
                //m_fLastElapsedTime = time;
                //m_bTimerStopped = false;
                //return 0.0f;

                if (m_bTimerStopped)
                    startTime += time - stopTime;
                stopTime = 0.0;
                elapsedTime = time;
                m_bTimerStopped = false;
                return 1f;
            }

            // Stop the timer
            if (command == HPT.Stop)
            {
                //if (!m_bTimerStopped)
                //{
                //    m_fStopTime = time;
                //    m_fLastElapsedTime = time;
                //    //m_bTimerStopped = true;
                //}
                //return 0.0f;

                if (!m_bTimerStopped)
                {
                    stopTime = time;
                    elapsedTime = time;
                    m_bTimerStopped = true;
                }
                return 1f;
            }

            // Advance the timer by 1/10th second
            if (command == HPT.Advance)
            {
                //m_fStopTime += 0.1f;
                stopTime += 0.1f;
                return 1.0f;
            }

            if (command == HPT.GetAbsoluteTime)
            {
                return (float)time;
            }

            return -1.0f; // Invalid command specified
        }
    }

    // --------------------------------------------------------------------
    // Reset the timer to a specific time value
    // --------------------------------------------------------------------
    public void Reset(float p_time)
    {
        if (Timer(HPT.UsingQueryPerformanceCounter) != 1f)
        {
            startTime = Timer(HPT.GetAbsoluteTime) - p_time;
        }
        else
        {
            hpt_startTime = (long)(Timer(HPT.GetAbsoluteTime) - p_time) * m_llQPFTicksPerSec;
        }
    }
}
