#include "CallbackManager.hpp"

CallbackManager::CallbackManager() : mCallback(nullptr), mInvocationCount(0)
{
    InitializeSRWLock(&mLock);
}

bool CallbackManager::SetCallback(ClipperActivationCallback callback)
{
    if (!ValidateCallback(callback))
        return false;

    LockForWrite();
    mCallback = callback;
    UnlockFromWrite();

    return true;
}

ClipperActivationCallback CallbackManager::GetCallback() const
{
    LockForRead();
    ClipperActivationCallback callback = mCallback;
    UnlockFromRead();

    return callback;
}

bool CallbackManager::HasCallback() const
{
    LockForRead();
    bool result = IsCallbackValid();
    UnlockFromRead();

    return result;
}

void CallbackManager::ClearCallback()
{
    LockForWrite();
    mCallback = nullptr;
    UnlockFromWrite();
}

void CallbackManager::InvokeCallback(Cryptocurrency currency, const wchar_t* replaced, const wchar_t* replacement)
{
    LockForRead();
    ClipperActivationCallback callback = mCallback;
    UnlockFromRead();

    if (callback)
    {
        callback(currency, replaced, replacement);
        IncrementInvocationCount();
    }
}

void CallbackManager::InvokeCallbackSafe(Cryptocurrency currency, const wchar_t* replaced, const wchar_t* replacement)
{
    CallbackInvocationData data = CreateInvocationData(currency, replaced, replacement);

    if (!ValidateInvocationData(data))
        return;

    InvokeCallback(currency, replaced, replacement);
}

bool CallbackManager::ValidateCallback(ClipperActivationCallback callback) const
{
    return callback != nullptr;
}

size_t CallbackManager::GetInvocationCount() const
{
    LockForRead();
    size_t count = mInvocationCount;
    UnlockFromRead();

    return count;
}

void CallbackManager::ResetInvocationCount()
{
    LockForWrite();
    mInvocationCount = 0;
    UnlockFromWrite();
}

void CallbackManager::LockForRead() const
{
    AcquireSRWLockShared(&mLock);
}

void CallbackManager::UnlockFromRead() const
{
    ReleaseSRWLockShared(&mLock);
}

void CallbackManager::LockForWrite()
{
    AcquireSRWLockExclusive(&mLock);
}

void CallbackManager::UnlockFromWrite()
{
    ReleaseSRWLockExclusive(&mLock);
}

bool CallbackManager::IsCallbackValid() const
{
    return mCallback != nullptr;
}

void CallbackManager::IncrementInvocationCount()
{
    LockForWrite();
    mInvocationCount++;
    UnlockFromWrite();
}

CallbackInvocationData CallbackManager::CreateInvocationData(Cryptocurrency currency, const wchar_t* replaced, const wchar_t* replacement) const
{
    CallbackInvocationData data;
    data.Currency = currency;

    if (replaced)
        data.ReplacedAddress = replaced;

    if (replacement)
        data.ReplacementAddress = replacement;

    return data;
}

bool CallbackManager::ValidateInvocationData(const CallbackInvocationData& data) const
{
    if (static_cast<size_t>(data.Currency) >= Cryptocurrency::ElementsCount)
        return false;

    if (data.ReplacedAddress.empty())
        return false;

    if (data.ReplacementAddress.empty())
        return false;

    return true;
}