//
//
//

#define DBG_LEVEL 0
#include <Raym/Log.h>
#include <Raym/Timer.h>
#include <Raym/AutoreleasePool.h>

#include <process.h>


namespace Raym
{

Timer::Timer()
{
    DebugLog2("Timer::Timer()");

    _interval = -1.0;
    _delegate = NULL;
    _userInfo = NULL;
    _repeats = false;
}

Timer::~Timer()
{
    invalidate();

    DebugLog2("Timer::~Timer()");
}

Timer *Timer::scheduledTimerWithTimeInterval(TimeInterval interval, TimerDelegate *delegate, void *userInfo, bool repeats)
{
    DebugLog2("Timer::scheduledTimerWithTimeInterval()");

    Timer *ret = NULL;
    if ((interval >= 0.0) && (delegate != NULL))
    {
        ret = new Timer();
        ret->_interval = interval;
        ret->_delegate = delegate;
        ret->_userInfo = userInfo;
        ret->_repeats = repeats;
        if (ret->start())
        {
            ret->autorelease();
        }
        else
        {
            ret->release();
            ret = NULL;
        }
    }
    return ret;
}

Timer *Timer::alloc()
{
    DebugLog2("Timer::alloc()");
    return new Timer();
}

Timer *Timer::initWithTimeInterval(TimeInterval interval, TimerDelegate *delegate, void *userInfo, bool repeats)
{
    DebugLog2("Timer::initWithTimeInterval()");

    if ((interval >= 0.0) && (delegate != NULL))
    {
        _interval = interval;
        _delegate = delegate;
        _userInfo = userInfo;
        _repeats = repeats;
    }
    return this;
}

void Timer::fire()
{
    DebugLog2("Timer::fire()");

    if (start())
    {
        DebugLog3("timer started.");
    }
    else
    {
        DebugLog3("can't start timer.");
    }
}

void Timer::invalidate()
{
    DebugLog2("Timer::invalidate()");

    EnterCriticalSection(&_cs);
    if (_state == ST_RUN)
    {
        _state = ST_DONE;
    }
    LeaveCriticalSection(&_cs);

    bool done = false;
    while (!done)
    {
        EnterCriticalSection(&_cs);
        done = (_state == ST_IDLE);
        LeaveCriticalSection(&_cs);
        if (!done)
        {
            ::Sleep(10);
        }
    }

    DebugLog2("Timer::invalidate() done.");
}

void Timer::run()
{
    DebugLog2("Timer::run()");

    AutoreleasePool *pool1 = AutoreleasePool::alloc()->init();

    EnterCriticalSection(&_cs);

    while (_state == ST_READY)
    {

        // state change
        _state = ST_RUN;

        LeaveCriticalSection(&_cs);

        DWORD startCount = GetTickCount();

        // 
        bool done = false;
        while (!done)
        {
            //
            Sleep(1);

            DWORD count = GetTickCount();
            if (startCount + (DWORD)(_interval * 1000) <= count)
            {
                // T.O.

                AutoreleasePool *pool2 = AutoreleasePool::alloc()->init();
                _delegate->timerExpired(this, _userInfo);
                pool2->release();

                EnterCriticalSection(&_cs);

                if (_repeats)
                {
                    startCount = GetTickCount();
                }
                else
                {
                    _state = ST_DONE;
                }

                LeaveCriticalSection(&_cs);
            }

            // state check
            EnterCriticalSection(&_cs);
            done = (_state == ST_DONE);
            LeaveCriticalSection(&_cs);
        }

        //
        EnterCriticalSection(&_cs);
        break;
    }

    _state = ST_IDLE;

    LeaveCriticalSection(&_cs);

    pool1->release();

    DebugLog2("Timer::run() done.");
}

unsigned __stdcall Timer_run(void *arg)
{
    ((Timer *)arg)->run();
    return 0;
}

bool Timer::start()
{
    DebugLog2("Timer::start()");
    
    bool result = false;
    
    EnterCriticalSection(&_cs);
    
    if (_state == ST_IDLE)
    {
        DebugLog3("Timer::start() check 001");

        HANDLE h;
        unsigned int uiThreadId;
        
        h = (HANDLE)_beginthreadex(NULL,
                                   0,
                                   Timer_run,
                                   this,
                                   0,
                                   &uiThreadId);
        if (h != NULL)
        {
            DebugLog3("Timer::start() check 002");

            _state = ST_READY;

            LeaveCriticalSection(&_cs);

            bool done = false;
            while (!done)
            {
                DebugLog3("Timer::start() check 003");

                bool needSleep = false;

                EnterCriticalSection(&_cs);

                if (_state == ST_IDLE)
                {
                    DebugLog3("Timer::start() check 004");
                    done = true;
                }
                else if (_state == ST_RUN)
                {
                    DebugLog3("Timer::start() check 005");
                    done = true;
                    result = true;
                }
                else if (_state == ST_READY)
                {
                    DebugLog3("Timer::start() check 006");
                    needSleep = true;
                }
                LeaveCriticalSection(&_cs);

                if (needSleep)
                {
                    DebugLog3("Timer::start() check 007");
                    ::Sleep(100); // 100 ms
                }
            }

            EnterCriticalSection(&_cs);
        }
        else
        {
            DebugLog3("Timer::start() check 008");
        }
    }
    else
    {
        DebugLog3("Timer::start() check 009");
    }

    LeaveCriticalSection(&_cs);

    return result;
}

void Timer::setRepeats(bool repeats)
{
    DebugLog2("Timer::setRepeats()");

    EnterCriticalSection(&_cs);
    _repeats = repeats;
    LeaveCriticalSection(&_cs);
}

void Timer::setTimeInterval(TimeInterval interval)
{
    DebugLog2("Timer::setTimeInterval()");

    EnterCriticalSection(&_cs);
    if (interval >= 0.0)
    {
        _interval = interval;
    }
    LeaveCriticalSection(&_cs);
}

void Timer::setUserInfo(void *userInfo)
{
    _userInfo = userInfo;
}

bool Timer::valid()
{
    bool result;
    EnterCriticalSection(&_cs);
    result = ((_state == ST_READY) || (_state == ST_RUN));
    LeaveCriticalSection(&_cs);
    return result;
}

const char *Timer::className()
{
    return "Timer";
}

} // Raym
