#include "SessionManager.h"
using namespace drogon;
SessionManager::SessionManager(
    trantor::EventLoop *loop,
    size_t timeout,
    const std::vector<AdviceStartSessionCallback> &startAdvices,
    const std::vector<AdviceDestroySessionCallback> &destroyAdvices,
    IdGeneratorCallback idGeneratorCallback)
    : loop_(loop),
      timeout_(timeout),
      sessionStartAdvices_(startAdvices),
      sessionDestroyAdvices_(destroyAdvices),
      idGeneratorCallback_(idGeneratorCallback)
{
    if (timeout_ > 0)
    {
        size_t wheelNum = 1;
        size_t bucketNum = 0;
        if (timeout_ < 500)
        {
            bucketNum = timeout_ + 1;
        }
        else
        {
            auto tmpTimeout = timeout_;
            bucketNum = 100;
            while (tmpTimeout > 100)
            {
                ++wheelNum;
                tmpTimeout = tmpTimeout / 100;
            }
        }
        sessionMapPtr_ = std::unique_ptr<CacheMap<std::string, SessionPtr>>(
            new CacheMap<std::string, SessionPtr>(
                loop_,
                1.0,
                wheelNum,
                bucketNum,
                [this](const std::string &key) {
                    for (auto &advice : sessionStartAdvices_)
                    {
                        advice(key);
                    }
                },
                [this](const std::string &key) {
                    for (auto &advice : sessionDestroyAdvices_)
                    {
                        advice(key);
                    }
                }));
    }
    else if (timeout_ == 0)
    {
        sessionMapPtr_ = std::unique_ptr<CacheMap<std::string, SessionPtr>>(
            new CacheMap<std::string, SessionPtr>(
                loop_,
                0,
                0,
                0,
                [this](const std::string &key) {
                    for (auto &advice : sessionStartAdvices_)
                    {
                        advice(key);
                    }
                },
                [this](const std::string &key) {
                    for (auto &advice : sessionDestroyAdvices_)
                    {
                        advice(key);
                    }
                }));
    }
}
SessionPtr SessionManager::getSession(const std::string &sessionID,
                                      bool needToSet)
{
    assert(!sessionID.empty());
    SessionPtr sessionPtr;
    sessionMapPtr_->modify(
        sessionID,
        [&sessionPtr, &sessionID, needToSet](SessionPtr &sessionInCache) {
            if (sessionInCache)
            {
                sessionPtr = sessionInCache;
            }
            else
            {
                sessionPtr =
                    std::shared_ptr<Session>(new Session(sessionID, needToSet));
                sessionInCache = sessionPtr;
            }
        },
        timeout_);
    return sessionPtr;
}
void SessionManager::changeSessionId(const SessionPtr &sessionPtr)
{
    auto oldId = sessionPtr->sessionId();
    auto newId = idGeneratorCallback_();
    sessionPtr->setSessionId(newId);
    sessionMapPtr_->insert(newId, sessionPtr, timeout_);
    sessionMapPtr_->runAfter(10, [this, oldId = std::move(oldId)]() {
        LOG_TRACE << "remove the old slot of the session";
        sessionMapPtr_->erase(oldId);
    });
}