// queue.cpp - written and placed in the public domain by Wei Dai

#include "pch.h"
#include "queue.h"
#include "misc.h"

// this class for use by ByteQueue only
class ByteQueueNode
{
public:
    ByteQueueNode(unsigned int maxSize);

    unsigned int CurrentSize() const
        {return tail-head;}
    unsigned int UsedUp() const
        {return (head==MaxSize());}

    unsigned int Put(byte inByte);
    unsigned int Put(const byte *inString, unsigned int length);

    unsigned int Get(byte &outByte);
    unsigned int Get(byte *outString, unsigned int getMax);

    unsigned int Peek(byte &outByte) const;

    void CopyTo(BufferedTransformation &target) const
        {target.Put(buf+head, tail-head);}
    void CopyTo(byte *target) const
        {memcpy(target, buf+head, tail-head);}

    byte operator[](unsigned int i) const
        {return buf[i-head];}

    ByteQueueNode *next;

private:
    unsigned int MaxSize() const {return buf.size;}

    SecByteBlock buf;
    unsigned int head, tail;
};


ByteQueueNode::ByteQueueNode(unsigned int maxSize)
    : buf(maxSize)
{
    head = tail = 0;
    next = 0;
}

unsigned int ByteQueueNode::Put(byte inByte)
{
    if (MaxSize()==tail)
        return 0;

    buf[tail++]=inByte;
    return 1;
}

unsigned int ByteQueueNode::Put(const byte *inString, unsigned int length)
{
    unsigned int l = STDMIN(length, MaxSize()-tail);
    memcpy(buf+tail, inString, l);
    tail += l;
    return l;
}

unsigned int ByteQueueNode::Get(byte &outByte)
{
    if (tail==head)
        return 0;

    outByte=buf[head++];
    return 1;
}

unsigned int ByteQueueNode::Get(byte *outString, unsigned int getMax)
{
    unsigned int l = STDMIN(getMax, tail-head);
    memcpy(outString, buf+head, l);
    head += l;
    return l;
}

unsigned int ByteQueueNode::Peek(byte &outByte) const
{
    if (tail==head)
        return 0;

    outByte=buf[head];
    return 1;
}

// ********************************************************

ByteQueue::ByteQueue(unsigned int nodeSize)
    : nodeSize(nodeSize)
{
    head = tail = new ByteQueueNode(nodeSize);
}

ByteQueue::ByteQueue(const ByteQueue &copy)
{
    CopyFrom(copy);
}

void ByteQueue::CopyFrom(const ByteQueue &copy)
{
    nodeSize = copy.nodeSize;
    head = tail = new ByteQueueNode(*copy.head);

    for (ByteQueueNode *current=copy.head->next; current; current=current->next)
    {
        tail->next = new ByteQueueNode(*current);
        tail = tail->next;
    }

    tail->next = NULL;
}

ByteQueue::~ByteQueue()
{
    Destroy();
}

void ByteQueue::Destroy()
{
    ByteQueueNode *next;

    for (ByteQueueNode *current=head; current; current=next)
    {
        next=current->next;
        delete current;
    }
}

void ByteQueue::CopyTo(BufferedTransformation &target) const
{
    for (ByteQueueNode *current=head; current; current=current->next)
        current->CopyTo(target);
}

void ByteQueue::CopyTo(byte *target) const
{
    for (ByteQueueNode *current=head; current; current=current->next)
    {
        current->CopyTo(target);
        target += current->CurrentSize();
    }
}

unsigned long ByteQueue::CurrentSize() const
{
    unsigned long size=0;

    for (ByteQueueNode *current=head; current; current=current->next)
        size += current->CurrentSize();

    return size;
}

void ByteQueue::Put(byte inByte)
{
    if (!tail->Put(inByte))
    {
        tail->next = new ByteQueueNode(nodeSize);
        tail = tail->next;
        tail->Put(inByte);
    }
}

void ByteQueue::Put(const byte *inString, unsigned int length)
{
    unsigned int l;

    while ((l=tail->Put(inString, length)) < length)
    {
        tail->next = new ByteQueueNode(nodeSize);
        tail = tail->next;
        inString += l;
        length -= l;
    }
}

unsigned int ByteQueue::Get(byte &outByte)
{
    int l = head->Get(outByte);
    if (head->UsedUp())
    {
        ByteQueueNode *temp=head;
        head = head->next;
        delete temp;
        if (!head)  // just deleted the last node
            head = tail = new ByteQueueNode(nodeSize);
    }
    return l;
}

unsigned int ByteQueue::Get(byte *outString, unsigned int getMax)
{
    unsigned int getMaxSave=getMax;
    ByteQueueNode *current=head;

    while (getMax && current)
    {
        int l=current->Get(outString, getMax);

        outString += l;
        getMax -= l;

        current = current->next;
    }

    while (head && head->UsedUp())
    {
        current=head;
        head=head->next;
        delete current;
    }

    if (!head)  // every single node has been used up and deleted
        head = tail = new ByteQueueNode(nodeSize);

    return (getMaxSave-getMax);
}

unsigned int ByteQueue::Peek(byte &outByte) const
{
    return head->Peek(outByte);
}

ByteQueue & ByteQueue::operator=(const ByteQueue &rhs)
{
    Destroy();
    CopyFrom(rhs);
    return *this;
}

bool ByteQueue::operator==(const ByteQueue &rhs) const
{
    const unsigned long currentSize = CurrentSize();

    if (currentSize != rhs.CurrentSize())
        return false;

    for (unsigned long i = 0; i<currentSize; i++)
        if ((*this)[i] != rhs[i])
            return false;

    return true;
}

byte ByteQueue::operator[](unsigned long i) const
{
    for (ByteQueueNode *current=head; current; current=current->next)
    {
        if (i < current->CurrentSize())
            return (*current)[i];
        
        i -= current->CurrentSize();
    }

    // i should be less than CurrentSize(), therefore we should not be here
    assert(false);
    return 0;
}
