//
// The developer of the original code and/or files is Tripwire, Inc.
// Portions created by Tripwire, Inc. are copyright (C) 2000 Tripwire,
// Inc. Tripwire is a registered trademark of Tripwire, Inc.  All rights
// reserved.
// 
// This program is free software.  The contents of this file are subject
// to the terms of the GNU General Public License as published by the
// Free Software Foundation; either version 2 of the License, or (at your
// option) any later version.  You may redistribute it and/or modify it
// only in compliance with the GNU General Public License.
// 
// This program is distributed in the hope that it will be useful.
// However, this program is distributed AS-IS WITHOUT ANY
// WARRANTY; INCLUDING THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS
// FOR A PARTICULAR PURPOSE.  Please see the GNU General Public License
// for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
// USA.
// 
// Nothing in the GNU General Public License or any other license to use
// the code or files shall permit you to use Tripwire's trademarks,
// service marks, or other intellectual property without Tripwire's
// prior written consent.
// 
// If you have any questions, please contact Tripwire, Inc. at either
// info@tripwire.org or www.tripwire.org.
//
///////////////////////////////////////////////////////////////////////////////
// bytequeue.cpp	- written and placed in the public domain by Wei Dai
//					- modified 29 Oct 1998 mdb

#include "stdtwcrypto.h"

#include "bytequeue.h"
#include "cryptlib/queue.h"
#include "cryptlib/misc.h"

//-----------------------------------------------------------------------------
// cByteQueueNode
//		this class for use by cByteQueue only
//-----------------------------------------------------------------------------
class cByteQueueNode
{
public:
	cByteQueueNode(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];}

	cByteQueueNode *next;

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

	SecByteBlock buf;
	unsigned int head, tail;
};


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

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

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

unsigned int cByteQueueNode::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 cByteQueueNode::Get(byte &outByte)
{
	if (tail==head)
		return 0;

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

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

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

	outByte=buf[head];
	return 1;
}

//-----------------------------------------------------------------------------
// cByteQueue
//-----------------------------------------------------------------------------

cByteQueue::cByteQueue(int mNodeSize)
	:	mNodeSize(mNodeSize),
		mCurrentSize(0)
{
	head = tail = new cByteQueueNode(mNodeSize);
}

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

void cByteQueue::CopyFrom(const cByteQueue &copy)
{
	mNodeSize		= copy.mNodeSize;
	mCurrentSize	= copy.mCurrentSize;

	head = tail = new cByteQueueNode(*copy.head);

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

	tail->next = NULL;
}

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

void cByteQueue::Destroy()
{
	cByteQueueNode *next;

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

	mCurrentSize = 0;
}

///////////////////////////////////////////////////////////////////////////////
// CopyTo -- note that this does not remove anything from the queue
///////////////////////////////////////////////////////////////////////////////
void cByteQueue::CopyTo(BufferedTransformation &target) const
{
	for (cByteQueueNode *current=head; current; current=current->next)
		current->CopyTo(target);
}

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

unsigned long cByteQueue::CurrentSize() const
{
	return mCurrentSize;
	/*
	unsigned long size=0;

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

	return size;
	*/
}

void cByteQueue::Put(byte inByte)
{
	if (!tail->Put(inByte))
	{
		tail->next = new cByteQueueNode(mNodeSize);
		tail = tail->next;
		tail->Put(inByte);
	}
	// put increases the size of the queue by one
	mCurrentSize++;
}

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

	// put increases the size of the queue by length
	mCurrentSize += length;

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

unsigned int cByteQueue::Get(byte &outByte)
{
	int l = head->Get(outByte);
	if (head->UsedUp())
	{
		cByteQueueNode *temp=head;
		head = head->next;
		delete temp;
		if (!head)  // just deleted the last node
			head = tail = new cByteQueueNode(mNodeSize);
	}
	// put decreases the size of the queue by one
	mCurrentSize--;

	return l;
}

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

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

		outString += l;
		getMax -= l;

		current = current->next;
	}

	// delete all used up nodes except the last one, to avoid the final new
	// that used to be below....
	while (head && head->UsedUp() && (head != tail))
	{
		current=head;
		head=head->next;
		delete current;
	}

	/*
	if (!head)  // every single node has been used up and deleted
		head = tail = new cByteQueueNode(mNodeSize);
	*/

	int rtn = getMaxSave-getMax;
	mCurrentSize -= rtn;

	return (rtn);
}

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

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

bool cByteQueue::operator==(const cByteQueue &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 cByteQueue::operator[](unsigned long i) const
{
	for (cByteQueueNode *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;
}

