/*ident	"@(#)cls4:lib/task/task/qhead.c	1.1" */
/*******************************************************************************
 
C++ source for the C++ Language System, Release 3.0.  This product
is a new release of the original cfront developed in the computer
science research center of AT&T Bell Laboratories.

Copyright (c) 1991 AT&T and UNIX System Laboratories, Inc.
Copyright (c) 1984, 1989, 1990 AT&T.  All Rights Reserved.

THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE of AT&T and UNIX System
Laboratories, Inc.  The copyright notice above does not evidence
any actual or intended publication of such source code.

*******************************************************************************/
#include <task.h>

/*	a qhead's qh_queue has its pointer q_ptr pointing the last
	element of a circular list, so that q_ptr->o_next is the
	first element of that list.

   STRUCTURE:
	qhead <--> oqueue <--> qtail	(qhead and qtail are independent)
	oqueue --> circular queue of objects

	"pen and paper is recommended when trying to understand
	the list manipulations."
*/

/* construct qhead <--> (possible)oqueue --> 0 */
qhead::qhead(qmodetype mode, int max)
{
	if (0 < max) {
		qh_queue = new oqueue(max);
		qh_queue->q_head = this;
	};
	qh_mode = mode;
}


/* destroy q if not pointed to by a qtail */
qhead::~qhead()
{
	oqueue* q = qh_queue;

	if (q->q_tail)
		q->q_head = 0;
	else 
		delete q;
	
}

/* remove and return object from head of q */

// q->q_ptr points to last object.  last->o_next points to first object.
// first->o_next points to the next object.
object*
qhead::get()
{
	register oqueue* q = qh_queue;
ll:
	if (q->q_count) {
		register object* oo = q->q_ptr;
		register object* p = oo->o_next;
		oo->o_next = p->o_next;
		p->o_next = 0;
		if (q->q_count-- == q->q_max) {
			qtail* t = q->q_tail;
			if (t) t->alert();
		};
		return p;
	}

	switch (qh_mode) {
	case WMODE:
		this_task()->sleep(this);
		goto ll;
	case EMODE:
		task_error(E_GETEMPTY, this);
		goto ll;
	case ZMODE:
		return 0;
	}
}

/* create a tail for this queue */
qtail*
qhead::tail()
{
	oqueue* q = qh_queue;
	register qtail* t = q->q_tail;

	if (t == 0) {
		t = new qtail(qh_mode,0);
		q->q_tail = t;
		t->qt_queue = q;
	}

	return t;
}


/* make room for a filter upstream from this qhead */
/* result:  (this)qhead<-->newq    (new)qhead<-->oldq ?<-->qtail? */
qhead*
qhead::cut()
{
	oqueue* oldq = qh_queue;
	qhead* h = new qhead(qh_mode,oldq->q_max);
	oqueue* newq = h->qh_queue;

	oldq->q_head = h;
	h->qh_queue = oldq;

	qh_queue = newq;
	newq->q_head = this;

	return h;
}


/* this qhead is supposed to be upstream to the qtail t
   add the contents of this's queue to t's queue
   destroy this, t, and this's queue
   alert the spliced qhead and qtail if a significant state change happened
*/
void
qhead::splice(qtail* t)
{
	oqueue* qt = t->qt_queue;
	oqueue* qh = qh_queue;

	int qtcount = qt->q_count;
	int qhcount = qh->q_count;
	int halert = (qtcount==0 && qhcount);	/* becomes non-empty */
	int talert = (qh->q_max <= qhcount && qhcount+qtcount<qt->q_max);
						/* becomes non-full */	
	if (qhcount) {
		object* ooh = qh->q_ptr;
		object* oot = qt->q_ptr;
		qt->q_ptr = ooh;
		if (qtcount) {			/* add the contents of qh to qt */
			object* tf = oot->o_next;	// t's first object
			oot->o_next = ooh->o_next;
			ooh->o_next = tf;
		}
		qt->q_count = qhcount + qtcount;
		qh->q_count = 0;
	}

	(qh->q_tail)->qt_queue = qt;
	delete t;
	qt->q_tail = qh->q_tail;
	qh->q_tail = 0;

	delete this;

	if (halert) qt->q_head->alert();
	if (talert) qt->q_tail->alert();
}


/* insert new object at head of queue (after queue->q_ptr) */
int
qhead::putback(object* p)
{
	oqueue* q = qh_queue;

	if (p->o_next) task_error(E_BACKOBJ, this);
ll:
	if (q->q_count++ < q->q_max) {
		if (q->q_count == 1) {
			q->q_ptr = p;
			p->o_next = p;
		}
		else {
			object* oo = q->q_ptr;
			p->o_next = oo->o_next;
			oo->o_next = p;
		}
		return 1;
	}

	switch (qh_mode) {
	case WMODE:
	case EMODE:
		task_error(E_BACKFULL, this);
		goto ll;
	case ZMODE:
		return 0;
	}
}

void
qhead::print(int n, int baseClass)
{
	if (!baseClass)
		printf("qhead ");

	oqueue* q = qh_queue;

	printf("mode=%d, max=%d, count=%d, tail=%d\n",
		qh_mode, q->q_max, q->q_count, q->q_tail);

	if (n&VERBOSE) {
		int m = n & ~(CHAIN|VERBOSE);
		if (q->q_tail) {
			printf("\ttail of queue:\n");
			q->q_tail->print(m);
		} else printf("\tno tail\n");
		q->print(m);
	}

	object::print(n, 1);
}


void
oqueue::print(int n)
{
	object* p = q_ptr;

	if (q_count == 0) return;

	printf("\tobject on queue:\n");

	do {
		p->print(n);
		p = p->o_next;
	} while (p != q_ptr);

	printf("\n");
}
