
#include "common/dispatcher.h"
#include <config/xf-config.h>

#if (USE_XF_COMMON_TIMEOUTMANAGER_CLASS != 0)

#include <cassert>
#include "xf/interface/behavior.h"
#include "xf/interface/mutex.h"
#include "timeoutmanager.h"
#if defined(XF_TRACE_EVENT_PUSH_POP) && (XF_TRACE_EVENT_PUSH_POP != 0)
    #include "trace/trace.h"
#endif // XF_TRACE_EVENT_PUSH_POP

using Mutex = interface::XFMutex;       // Rename XFMutex interface class to Mutex for easier use.

// Implementation of the getInstance() method of the 'interface::XFTimeoutManager' class.
//
// Note: The implementation is done here because only in this file the real XFTimeoutManager
//       class is known (port specific class). An instance of the XFTimeoutManager class is
//       returned by the 'interface::XFTimeoutManager' class.
interface::XFTimeoutManager * interface::XFTimeoutManager::getInstance() {
    static ::XFTimeoutManager timeoutManager;
    return &timeoutManager;
}

XFTimeoutManager::XFTimeoutManager() {
    this->pMutex_ = interface::XFMutex::create();
}

XFTimeoutManager::~XFTimeoutManager() {

}

void XFTimeoutManager::addTimeout(XFTimeout *pNewTimeout) {

    const int newTime = pNewTimeout->getInterval();
    int totalTime = 0;
    bool isEnd = true;
    int lastTime = 0;

    this->pMutex_->lock();
    TimeoutList::iterator it = this->timeouts_.begin();
    isEnd = (it == this->timeouts_.end());

    if(!isEnd) totalTime += (*it)->getRelTicks();

    while(!isEnd && (totalTime <= newTime)) {
        isEnd = (++it == this->timeouts_.end());
        lastTime = totalTime;
        if(!isEnd) totalTime += (*it)->getRelTicks();
    }

    if(!isEnd) (*it)->substractFromRelTicks(newTime-lastTime);

    pNewTimeout->setRelTicks(newTime-lastTime);
    this->timeouts_.insert(it, pNewTimeout);

    this->pMutex_->unlock();

}

void XFTimeoutManager::returnTimeout(XFTimeout *pTimeout) {
    this->pMutex_->lock();
    XFDispatcher::getInstance()->pushEvent(pTimeout);
    this->timeouts_.remove(pTimeout);
    this->pMutex_->unlock();
}

void XFTimeoutManager::start(std::function<void (uint32_t)> startTimeoutManagerTimer) {
    startTimeoutManagerTimer(this->tickInterval_);
}

void XFTimeoutManager::scheduleTimeout(int32_t timeoutId, int32_t interval, interface::XFBehavior *pBehavior) {
    ::XFTimeout* timeout = new XFTimeout(timeoutId, interval, pBehavior);
    addTimeout(timeout);
}

void XFTimeoutManager::unscheduleTimeout(int32_t timeoutId, interface::XFBehavior *pBehavior) {
    this->pMutex_->lock();
    TimeoutList::iterator it;
    for(it = this->timeouts_.begin(); it != this->timeouts_.end(); it++){

        if((*it)->getId()==timeoutId && (*it)->getBehavior() == pBehavior) {
            it = this->timeouts_.erase(it);
        }

    }
    this->pMutex_->unlock();
}

void XFTimeoutManager::tick() {

    bool isEmpty = this->timeouts_.empty();
    int rTime;

    if(!isEmpty) {

        this->pMutex_->lock();

        XFTimeout* timeout = this->timeouts_.front();
        timeout->substractFromRelTicks(tickInterval_);
        rTime = timeout->getRelTicks();

        while (!isEmpty && (rTime <= 0) ) {

            XFDispatcher::getInstance()->pushEvent(timeout);
            this->timeouts_.pop_front();

            timeout = this->timeouts_.front();
            isEmpty = this->timeouts_.empty();
            if(!isEmpty) rTime = timeout->getRelTicks();

        }

        this->pMutex_->unlock();

    }
}

#endif // USE_XF_COMMON_TIMEOUTMANAGER_CLASS
