#include "TickQueue.h"

#include "Block.h"

TickQueue::TickQueue()
    : ReferenceCounter()
{
    maxSize = 0;
    readPosition = 0;
    writePosition = 0;
    queue = 0;
    exit = 0;
}

TickQueue::~TickQueue()
{
    delete[] queue;
}

void TickQueue::startNextTick(Framework::Array<Block*>* zSources)
{
    std::unique_lock<std::mutex> lk(mutex);
    readPosition = 0;
    writePosition = 0;
    int count = zSources->getEintragAnzahl();
    if (count >= maxSize)
    {
        Block** temp = new Block*[count + 1000];
        memcpy(queue, temp, sizeof(Block*) * maxSize);
        memset(temp + sizeof(Block*) * maxSize,
            0,
            sizeof(Block*) * (count + 1000 - maxSize));
        maxSize = count + 1000;
        delete[] queue;
        queue = temp;
    }
    for (Block* block : *zSources)
        queue[writePosition++] = block;
    lk.unlock();
    hasBlocks.notify_all();
}

void TickQueue::addToQueue(Block* zBlock)
{
    std::unique_lock<std::mutex> lk(mutex);
    if (writePosition >= maxSize)
    {
        Block** temp = new Block*[maxSize + 1000];
        memcpy(queue, temp, sizeof(Block*) * maxSize);
        memset(temp + sizeof(Block*) * maxSize, 0, sizeof(Block*) * 1000);
        maxSize += 1000;
        delete[] queue;
        queue = temp;
    }
    queue[writePosition++] = zBlock;
    lk.unlock();
    hasBlocks.notify_one();
}

Block* TickQueue::zNextBlock(bool& waiting)
{
    std::unique_lock<std::mutex> lk(mutex);
    if (readPosition == writePosition && exit) return 0;
    if (readPosition == writePosition)
    {
        lk.unlock();
        hasNoBlocks.notify_all();
        lk.lock();
        waiting = 1;
        hasBlocks.wait(
            lk, [this] { return readPosition < writePosition || exit; });
        waiting = 0;
    }
    if (readPosition < writePosition) return queue[readPosition++];
    return 0;
}

void TickQueue::requestExit()
{
    std::unique_lock<std::mutex> lk(mutex);
    exit = 1;
    lk.unlock();
    hasBlocks.notify_all();
    hasNoBlocks.notify_all();
}

void TickQueue::waitForEmpty()
{
    std::unique_lock<std::mutex> lk(mutex);
    if (readPosition != writePosition)
        hasNoBlocks.wait(
            lk, [this] { return readPosition == writePosition || exit; });
}

void TickQueue::postTick()
{
    for (int i = 0; i < writePosition; i++)
        queue[i]->postTick();
}