#include <string.h>
#include "Log.h"
#include "LockGuard.h"


namespace vendor   {
namespace samsung  {
namespace hardware {
namespace security {
namespace drk      {

timed_mutex LockGuard::mLock[MAX_LOCK];
time_t LockGuard::mStart = 0;
time_t LockGuard::mEnd = 0;

LockGuard::LockGuard() : mTimeOut(5000)
{
    Clear();
}

LockGuard::LockGuard(uint32_t timeline)
{
    (timeline > 0) ? mTimeOut = timeline : mTimeOut = 5000;
    Clear();
}

LockGuard::~LockGuard()
{
    Clear();
}

void LockGuard::Clear()
{
    mPrevpid = 0;
    mPrevcmd = 0;
}

bool LockGuard::lock(uint32_t idx)
{
    bool ret = false;

    if (mLock[idx].try_lock_for(std::chrono::milliseconds(mTimeOut))) {
        ret = true;
    }

    return ret;
}

void LockGuard::unlock(uint32_t idx)
{
    mLock[idx].unlock();
}

void LockGuard::startTimer()
{
    mStart = time(NULL);
}

bool LockGuard::checkTimer()
{
    int timeGap;
    mEnd    = time(NULL);
    timeGap = (int)difftime(mEnd, mStart);

    if (timeGap > 7) {
        return false;
    } else {
        return true;
    }
}

int32_t LockGuard::Enter(uint32_t cmd, uint32_t pid)
{
    int32_t ret = ERR_TIMEOUT_TO_LOCK;
    uint32_t cmdtype = 1;

    if (cmd < CMD_DRK_VND) {
        cmdtype = 0;
    }

    if (mPrevcmd != 0) {
        if (mPrevcmd == CMD_MAKE_DRK_CSR && checkTimer()) {
            if (cmd != CMD_INSTALL_DRK_CERT && cmd != CMD_MAKE_DRK_CSR) {
                LOGMI("cmd [%x] of pid [%d] can't get lock since cmd [%x] of pid [%d] is locked.",
                      cmd, pid, mPrevcmd, mPrevpid);
                ret = ERR_LOCK_TO_HOLD;
                goto end;
            }
        }

        if (mPrevcmd == CMD_GENERATE_SERVICE_KEY && checkTimer()) {
            if (cmd != CMD_RELEASE_GEN_SERV_KEY 
                    && ((cmd != CMD_GENERATE_SERVICE_KEY) || (mPrevpid != pid))
#ifndef USE_RELEASE
                    && cmd != CMD_VERIFY_SERVICE_KEY
#endif
            ) {
                LOGMI("cmd [%x] of pid [%d] can't get lock since cmd [%x] of pid [%d] is locked.",
                      cmd, pid, mPrevcmd, mPrevpid);
                ret = ERR_LOCK_TO_HOLD;
                goto end;
            }
        }
    }

    if (cmd == CMD_MAKE_DRK_CSR || cmd == CMD_GENERATE_SERVICE_KEY) {
        startTimer();
    }

    if (lock(cmdtype)) {
        LOGMD("cmd [%x] of pid [%d] is locked.", cmd, pid);
        mPrevpid = pid;
        mPrevcmd = cmd;
        ret = NOT_ERROR;
    } else {
        LOGMI("cmd [%x] of pid [%d] can't get lock since cmd [%x] of pid [%d] is locked.",
              cmd, pid, mPrevcmd, mPrevpid);
        ret = ERR_LOCK_TO_HOLD;
    }
end:
    return ret;
}

void LockGuard::Leave(uint32_t cmd, int32_t ret)
{
    uint32_t cmdtype = 1;
    LOGMD("cmd [%x] of pid [%d] release the lock.", mPrevcmd, mPrevpid);

    if(cmd < CMD_DRK_VND) {
        cmdtype = 0;
    }

    if((cmd != CMD_MAKE_DRK_CSR && cmd != CMD_GENERATE_SERVICE_KEY) ||
            ret != NOT_ERROR) {
        mPrevpid = 0;
        mPrevcmd = 0;
    }

    unlock(cmdtype);
}

}  // namespace drk
}  // namespace security
}  // namespace hardware
}  // namespace samsung
}  // namespace vendor
