/*==LICENSE==*

CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011  Cyan Worlds, Inc.

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.

You can contact Cyan Worlds, Inc. by email legal@cyan.com
 or by snail mail at:
      Cyan Worlds, Inc.
      14617 N Newport Hwy
      Mead, WA   99021

*==LICENSE==*/
#ifndef hsTimer_Defined
#define hsTimer_Defined

#include "hsWide.h"
#include "hsScalar.h"

#if !HS_CAN_USE_FLOAT
#error "Unsupported without double's"
#endif // !HS_CAN_USE_FLOAT

class plTimerShare
{
protected:
    mutable hsBool      fFirstTime;
    mutable hsWide      fRawTimeZero;
    mutable hsBool      fResetSmooth;

    enum {
        kSmoothBuffLen = 10
    };
    double              fSmoothBuff[kSmoothBuffLen];
    int                 fCurrSlot;

    hsScalar            fSysTimeScale;
    double              fRealSeconds;
    double              fSysSeconds;
    hsScalar            fDelSysSeconds;
    hsScalar            fFrameTimeInc;
    hsBool              fRunningFrameTime;
    hsScalar            fTimeClampSecs;
    hsScalar            fSmoothingClampSecs;
    hsBool              fClamping;

    hsWide*             FactorInTimeZero(hsWide* ticks) const;

    double              GetSeconds() const;
    double              GetMilliSeconds() const;

    hsWide*             GetRawTicks(hsWide* ticks) const;

    double              RawTicksToDSeconds(const hsWide& ticks);
    hsWide              DSecondsToRawTicks(double secs);

    hsScalar            GetDelSysSeconds() const { return fDelSysSeconds; }
    double              GetSysSeconds() const { return fSysSeconds; }
    double              IncSysSeconds();

    void                SetRealTime(hsBool realTime);
    hsBool              IsRealTime() const { return !fRunningFrameTime; }

    void                SetFrameTimeInc(hsScalar inc) { fFrameTimeInc = inc; }

    void                SetTimeScale(hsScalar s) { fSysTimeScale = s; }
    hsScalar            GetTimeScale() const { return fSysTimeScale; }

    void                SetTimeClamp(hsScalar secs) { fTimeClampSecs = secs; }
    void                SetSmoothingCap(hsScalar secs) { fSmoothingClampSecs = secs; }
    hsScalar            GetTimeClamp() const { return fTimeClampSecs; }
    hsBool              IsClamping() const { return fClamping; }

    friend class hsTimer;
public:
    plTimerShare();
    ~plTimerShare();
};

class hsTimer 
{
protected:
    static const double             fPrecTicksPerSec;
    static const hsWide             fRawBase;

    static  hsWide                  IInitRawBase();
    
    static plTimerShare*            fTimer;
public:

    static hsBool   VerifyRawBase() { return fRawBase == IInitRawBase(); }
    static  const hsWide&       GetRawBase() { return fRawBase; }

    static  hsWide*             GetRawTicks(hsWide* ticks) { return fTimer->GetRawTicks(ticks); }

    static  double      GetSeconds() { return fTimer->GetSeconds(); }
    static  double      GetMilliSeconds() { return fTimer->GetMilliSeconds(); }

    static double       RawTicksToDSeconds(const hsWide& ticks) { return fTimer->RawTicksToDSeconds(ticks); }
    static hsWide       DSecondsToRawTicks(double secs) { return fTimer->DSecondsToRawTicks(secs); }

    static hsScalar     GetDelSysSeconds() { return fTimer->GetDelSysSeconds(); }
    static double       GetSysSeconds() { return fTimer->GetSysSeconds(); }

    static double       IncSysSeconds() { return fTimer->IncSysSeconds(); }

    static void         SetRealTime(hsBool realTime) { fTimer->SetRealTime(realTime); }
    static hsBool       IsRealTime() { return fTimer->IsRealTime(); }

    static void         SetFrameTimeInc(hsScalar inc) { fTimer->SetFrameTimeInc(inc); }

    static void         SetTimeScale(hsScalar s) { fTimer->SetTimeScale(s); }
    static hsScalar     GetTimeScale() { return fTimer->GetTimeScale(); }

    static void         SetTimeClamp(hsScalar secs) { fTimer->SetTimeClamp(secs); }
    static void         SetTimeSmoothingClamp(hsScalar secs) { fTimer->SetSmoothingCap(secs); }
    static hsScalar     GetTimeClamp() { return fTimer->GetTimeClamp(); }
    static hsBool       IsClamping() { return fTimer->IsClamping(); }

    ///////////////////////////
    // Precision timer routines - these are stateless and implemented as statics.
    ///////////////////////////
    static UInt32   GetPrecTickCount();
    static double   GetPrecTicksPerSec();
    static UInt32   PrecSecsToTicks(hsScalar secs);
    static double   PrecTicksToSecs(UInt32 ticks);
    static double   PrecTicksToHz(UInt32 ticks);

    // If you need to time something longer than 20 seconds, use this instead of
    // the precision timer.  It works the same, it just gives you full resolution.
    static UInt64   GetFullTickCount();
    static float    FullTicksToMs(UInt64 ticks);

    //
    // Pass GetTheTimer() into other process space, and then call SetTheTimer() on it.
    static void             SetTheTimer(plTimerShare* timer);
    static plTimerShare*    GetTheTimer() { return fTimer; }
};



#endif