// $Id: Queue.cc,v 1.5 2003/08/31 00:38:11 vern Exp $
//
// Copyright (c) 1999, 2002
//      The Regents of the University of California.  All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that: (1) source code distributions
// retain the above copyright notice and this paragraph in its entirety, (2)
// distributions including binary code include the above copyright notice and
// this paragraph in its entirety in the documentation or other materials
// provided with the distribution, and (3) all advertising materials mentioning
// features or use of this software display the following acknowledgement:
// ``This product includes software developed by the University of California,
// Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
// the University nor the names of its contributors may be used to endorse
// or promote products derived from this software without specific prior
// written permission.
// THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
// WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.

#include "config.h"

#include <string.h>

#include "Queue.h"

BaseQueue::BaseQueue(int size)
	{
	const int DEFAULT_CHUNK_SIZE = 10;

	chunk_size = DEFAULT_CHUNK_SIZE;

	head = tail = num_entries = 0;

	if ( size < 0 )
		{
		entry = new ent[1];
		max_entries = 0;
		}
	else
		{
		if ( (entry = new ent[chunk_size+1]) )
			max_entries = chunk_size;
		else
			{
			entry = new ent[1];
			max_entries = 0;
			}
		}
	}

void BaseQueue::push_front(ent a)
	{
	if ( num_entries == max_entries )
		{
		resize(max_entries+chunk_size);	// make more room
		chunk_size *= 2;
		}

	++num_entries;
	if ( head )
		entry[--head] = a;
	else
		{
		head = max_entries;
		entry[head] = a;
		}
	}

void BaseQueue::push_back(ent a)
	{
	if ( num_entries == max_entries )
		{
		resize(max_entries+chunk_size);	// make more room
		chunk_size *= 2;
		}

	++num_entries;
	if ( tail < max_entries )
		entry[tail++] = a;
	else
		{
		entry[tail] = a;
		tail = 0;
		}
	}

ent BaseQueue::pop_front()
	{
	if ( ! num_entries )
		return 0;

	--num_entries;
	if ( head < max_entries )
		return entry[head++];
	else
		{
		head = 0;
		return entry[max_entries];
		}
	}

ent BaseQueue::pop_back()
	{
	if ( ! num_entries )
		return 0;

	--num_entries;
	if ( tail )
		return entry[--tail];
	else
		{
		tail = max_entries;
		return entry[tail];
		}
	}

int BaseQueue::resize(int new_size)
	{
	if ( new_size < num_entries )
		new_size = num_entries; // do not lose any entries

	if ( new_size != max_entries )
		{
		// Note, allocate extra space, so that we can always
		// use the [max_entries] element.
		// ### Yin, why not use realloc()?
		ent* new_entry = new ent[new_size+1];

		if ( new_entry )
			{
			if ( head <= tail )
				memcpy( new_entry, entry + head,
					sizeof(ent) * num_entries );
			else
				{
				int len = num_entries - tail;
				memcpy( new_entry, entry + head,
					sizeof(ent) * len );
				memcpy( new_entry + len, entry,
					sizeof(ent) * tail );
				}
			delete [] entry;
			entry = new_entry;
			max_entries = new_size;
			head = 0;
			tail = num_entries;
			}
		else
			{ // out of memory
			}
		}

	return max_entries;
	}
