#include	"misc.h"
#include	"slug.h"
#include	"range.h"
#include	"page.h"

queue	squeue;
queue	bfqueue;
queue	ufqueue;

// We use the stream function current() to access a queue's head.
// Thus, queue member curr should always point to its first range.
void queue::check(char *whence)
{
	if (dbg & 8) {
		char *p;
		if (this == &squeue)
			p = "squeue";
		else if (this == &bfqueue)
			p = "bfqueue";
		else if (this == &ufqueue)
			p = "ufqueue";
		else
			p = "weird queue";
		printf("#checking %s\n", p);
	}
	if (first != curr)
		ERROR "check(%s): first != curr, line %d\n", whence, curr->rp->lineno() FATAL;
}

// When ranges are told to enqueue themselves, they are being rejected from the
// stage back onto their original queues.
// They reset any parameters that may have been altered by staging or trial
// composition.

void	range::enqueue(int block)
{
	squeue.enqueue(this);
	if (block)
		squeue.block();
}

void	ufrange::enqueue(int block)
{
	restore();			// both goal positions
	ufqueue.enqueue(this);
	if (block)
		ufqueue.block();
}

void	bfrange::enqueue(int block)
{
	restore();			// both goal positions
	bfqueue.enqueue(this);
	if (block)
		bfqueue.block();
}

int anymore()
{
	return !(squeue.empty() && ufqueue.empty() && bfqueue.empty());
}

void mergestream::unblock()
{
	squeue.unblock();
	bfqueue.unblock();
	ufqueue.unblock();
}

// Fill the staging area with a minimal chunk of input ranges.
int mergestream::prime()
{
	if (dbg & 4)
		printf("#entering mergestream::prime()\n");
	if (!empty())
		return 1;
	int brkok = 1;			// is it OK to break after the last
					// VBOX that was added to the stage?
	int needheight = -1;		// minimum acceptable height of the
					// chunk being constructed on stage
	// If the range at the head of any queue is breaking,
	// deal with it first.
	if (squeue.more() && squeue.current()->breaking())
		enqueue(squeue.dequeue());
	else if (bfqueue.more() && (bfqueue.current()->breaking() ||
		(bfqueue.serialno() < squeue.serialno())))
		enqueue(bfqueue.dequeue());
	else if (ufqueue.more() && (ufqueue.current()->breaking() ||
		(ufqueue.serialno() < squeue.serialno())))
		enqueue(ufqueue.dequeue());
	else while (squeue.more()) {
		// Fill the stage with enough ranges to be a valid chunk.
		range *r = squeue.dequeue();
		if (r->isvbox()) {	// VBOX
			if (dbg & 16)
				printf("#VBOX: !empty: %d; brkok: %d; vsince: %d\n",
					!empty(), brkok, currpage->vsince);
			if (!empty()	// there's something there
				&& brkok
					// it's OK to break here
				&& currpage->vsince >= 2
					// enough stream has gone onto this page
				&& rawht() >= needheight
					// current need has been satisfied
				) {
					// the stage already contains enough
					// ranges, so this one can wait
				r->enqueue();
				break;
			} else {
				if (r->rawht() > 0) {
					++currpage->vsince;
					brkok = r->brkafter();
				}
				enqueue(r);
			}
		} else if (r->isnested() || r->issp()) {	// US, SP
			if (!empty() && rawht() >= needheight) {
					// enough already, wait
				r->enqueue();
				break;
			}
			currpage->vsince = 0;
			enqueue(r);
			if (height() >= needheight)
				break;
		} else if (r->isneed()) {	// NE
			if (!empty() && rawht() >= needheight) {
					// not currently working on an unsatisfied NEed 
				r->enqueue();
				break;
			}
					// deal with overlapping NEeds
			needheight = rawht() + max(needheight - rawht(), r->needht());
			enqueue(r);
		} else if (r->forceflush() == NO) {
			enqueue(r);
		} else if (r->forceflush() == YES) {
			currpage->vsince = 0;
			if (!empty()) {
					// ready or not, r must wait
				r->enqueue();
				break;
			}
			enqueue(r);
			break;
		} else
			ERROR "unexpected  %s[%s] in prime(), line %d\n",
				r->typename(), r->headstr(), r->lineno() FATAL;
	}
	return more();			// 0 if nothing was staged
}

void page::cmdproc()
{
	if (stage->next())
		ERROR "more than a single command on bsqueue\n" FATAL;
	switch (stage->current()->cmdtype()) {
	case FC:	// freeze the current 2-column range and start a new one
		adddef(stage->dequeue());
		twocol->compose(FINAL);
		adddef(twocol);
		twocol = new multicol(this);
		break;
	case BP:	// force a page break
		adddef(stage->dequeue());
		squeue.block();
		break;
	case FL:	// flush out all floatables that precede this range:
			// no more stream input allowed until they're past
		if (stage->serialno() > ufqueue.serialno() ||
			stage->serialno() > bfqueue.serialno()) {
			range *r = stage->dequeue();
			r->enqueue(ANDBLOCK);
		} else
			adddef(stage->dequeue());
		break;
	default:
		stage->current()->dump();
		ERROR "unknown command\n" FATAL;
	}
}

void page::parmproc()
{
	if (stage->next())
		ERROR "more than a single parameter on bsqueue\n" FATAL;
	switch (stage->current()->parmtype()) {
	case NP:	// page top margin
		if (blank())
			pagetop = stage->current()->parm();
		pagesize = pagebot - pagetop;
		break;
	case FO:
		if (blank())
			pagebot = stage->current()->parm();
		pagesize = pagebot - pagetop;
		break;
	case PL:
		if (blank())
			physbot = stage->current()->parm();
		break;
	case MF:
		minfull = 0.01*stage->current()->parm();
		break;
	case CT:
		coltol = 0.01*stage->current()->parm();
		break;
	case WARN:
		wantwarn = stage->current()->parm();
		break;
	case DBG:
		dbg = stage->current()->parm();
		break;
	default:
		stage->current()->dump();
		ERROR "unknown parameter\n" FATAL;
	}
	adddef(stage->dequeue());
}

// Process the contents of the staging area; a relic that used to do more.
void mergestream::pend()
{
	if (dbg & 4)
		printf("#entering mergestream::pend()\n");
	if (!more())
		return;
	if (current()->iscmd())
		currpage->cmdproc();
	else if (current()->isparm())
		currpage->parmproc();
	else
		currpage->tryout();
}
