const int	NOGOAL = -1;

class stream;

enum primeflush { NO, YES, EXPECTED, UNEXPECTED };	// mergestream::prime()

// Ranges do two things.  They interpose a layer between slugs and the rest
// of the program; this is important because of the grossness of the slug
// data structure (made necessary by its origins in troff output).  Ranges also
// group together other ranges into meaningful chunks like unbreakable stream
// objects, floatable objects, and page headers and footers.
// Member function height() returns a range's height as of the latest composition.
// Member function rawht() returns the range's original height in the input.
class range {
  protected:
	slug	*first;		// earliest slug in range
	int	accumV;		// accumulated V to this point
  public:
	range()		{ first = 0; accumV = 0; }
	range(slug *p)	{ first = p; accumV = 0; }
	virtual ~range() { }
	char	*headstr()		{
		return first ? first->headstr() : (char*)""; }
	char	*typename()		{ return first->typename(); }
	int	serialno()		{ return first->serialno(); }
	int	lineno()		{ return first->lineno(); }
	virtual void	dump()		{ first->dump(); }
	virtual void	rdump()		{ dump(); }
	virtual int	print(int cv, int col)	{
		first->slugout(col); return cv; }
	virtual int	floatable()	{ return 0; }
	virtual int	brkafter()	{ return 1; }
	virtual int	isnested()	{ return 0; }
	virtual int	issp()		{ return 0; }
	virtual int	isvbox()	{ return 0; }
	virtual int	isneed()	{ return 0; }
	virtual int	iscmd()		{ return 0; }
	virtual int	cmdtype()	{ return -1; }
	virtual int	isparm()	{ return 0; }
	virtual int	parmtype()	{ return -1; }
	virtual int	parm()		{ return -1; }
	virtual int	breakable()	{ return 0; }
	virtual int	forceflush()	{ return UNEXPECTED; }
	virtual int	pn()		{ return 0; }
	virtual stream	*children()	{ return 0; }	// see page::peeloff()
	virtual void	killkids()	{ }
	virtual void	enqueue(int = 0);
	virtual int	height()	{ return 0; }
	virtual int	rawht()		{ return 0; }
	virtual int	needht()	{ return 0; }
	virtual void	reheight(int *, int *)	{ }
	virtual void	rerawht(int *, int *)	{ }
	virtual void	setheight(int) { }
	virtual void	restore()	{ }		// goals of floatables
	virtual int	goal()		{ return NOGOAL; }
	int		accum()		{ return accumV; }
	void		setaccum(int n)	{ accumV = n; }
	virtual	void	setgoal(int)	{ }
	virtual void	pickgoal(int, double)	{ }
	virtual int	numcol()	{ return first->numcol(); }
	virtual int	issentinel()	{ return 0; }
	virtual range	*clone()	{ return 0; }
	virtual int	breaking()	{ return 0; }
	virtual void	setbreaking()	{ }
};

class vboxrange : public range {
	int	dv;		// inherited from slug
	int	base;		// inherited from slug
	int	brk;		// 0 => ok to break after, 1 => no break
  public:
	vboxrange(slug *p) : range(p) { dv = p->dv; base = p->base; brk = p->parm; }
	void	dump() {
		printf("#### VBOX brk? %d dv %d ht %d\n", brk, dv, dv+base); }
	int	print(int cv, int col) {
		printf("V%d\n", cv += dv); first->slugout(col); return cv+base; }
	int	brkafter()		{ return !brk; }
	int	isvbox()		{ return 1; }
	int	forceflush()		{ return NO; }
	int	height()		{ return dv + base; }
	int	rawht()			{ return first->dv + first->base; }
	void	reheight(int *cv, int *mv) {
		*cv += dv+base; *mv = max(*mv, *cv); }
	void	rerawht(int *cv, int *mv) {
		*cv += rawht(); *mv = max(*mv, *cv); }
};

class sprange : public range {
	int dv;
  public:
	sprange(slug *p) : range(p) { dv = first->dv; }
	void	dump() {
		printf("#### SP dv %d (originally %d)\n", dv, first->dv); }
	int	print(int cv, int col)	{
		first->slugout(col); return cv + dv; }
	int	issp()			{ return 1; }
	int	forceflush()		{ return YES; }
	int	height()		{ return dv; }
	int	rawht()			{ return first->dv; }
	void	reheight(int *, int *);
	void	rerawht(int *, int *);
	void	setheight(int n)	{ dv = n; }
};

class tmrange : public range {
  public:
	tmrange(slug *p) : range(p)	{ }
	int	forceflush()		{ return NO; }
	int	print(int cv, int col)	{ first->slugout(col); return cv; }
};

class coordrange : public range {
  public:
	coordrange(slug *p) : range(p)	{ }
	int	forceflush()		{ return NO; }
	int	print(int cv, int col)
		{ first->slugout(col); printf(" Y %d\n", cv); return cv; }
};

class nerange : public range {
  public:
	nerange(slug *p) : range(p)	{ }
	int	isneed()		{ return 1; }
	int	forceflush()		{ return YES; }
	int	needht()		{ return first->dv; }
};

class mcrange : public range {
  public:
	mcrange(slug *p) : range(p)	{ }
	int	forceflush()		{ return YES; }
};

class cmdrange : public range {
  public:
	cmdrange(slug *p) : range(p)	{ }
	int	iscmd()			{ return 1; }
	int	forceflush()		{ return YES; }
	int	cmdtype()		{ return first->parm; }
};

class parmrange : public range {
  public:
	parmrange(slug *p) : range(p)	{ }
	int	isparm()		{ return 1; }
	int	forceflush()		{ return YES; }
	int	parmtype()		{ return first->parm; }
	int	parm()			{ return first->parm2; }
};

class bsrange : public range {
  public:
	bsrange(slug *p) : range(p)	{ }
	int	forceflush()		{ return NO; }
	int	print(int cv, int col)	{ first->slugout(col); return cv; }
};

class endrange : public range {
  public:
	endrange(slug *p) : range(p)	{ }
	int	forceflush()		{ return UNEXPECTED; }
};

class eofrange : public range {
  public:
	eofrange(slug *p) : range(p)	{ }
	int	forceflush()		{ return UNEXPECTED; }
};

extern eofrange *lastrange;	// the EOF block (trailer, etc.) goes here

int measure(stream *);
int rawmeasure(stream *);

// A nestrange packages together a sequence of ranges, its subrange.
// Other parts of the program reach in and alter the dimensions of
// some of these ranges, so when the height of a range is requested
// it is computed completely afresh.
// (Note:  the alternative, of keeping around many copies of ranges
// with different dimensions, was abandoned because of the difficulty
// of ensuring that exactly one copy of each original range would be
// output.)
class nestrange : public range {
  protected:
	stream	*subrange;
	int isbreaking;
	int rawdv;
  public:
	nestrange() : range()	{ subrange = 0; isbreaking = 0; rawdv = -1; }
	nestrange(slug *p, stream *s) : range(p)
				{ subrange = s; isbreaking = 0; rawdv = -1; }
	void	rdump();
	virtual void restore();
	stream	*children()	{ return subrange; }
	void	killkids();
	int	height()	{ return measure(subrange); }
	int	rawht()		{ if (rawdv < 0 || isbreaking) rawdv = rawmeasure(subrange);
					return rawdv; }
	void	reheight(int *cv, int *mv) {
			*mv += measure(subrange); *cv = max(*mv, *cv); }
	void	rerawht(int *cv, int *mv) {
			*mv += rawht(); *cv = max(*mv, *cv); }
	int	isnested()	{ return 1; }
	int	forceflush()	{ return EXPECTED; }
	int	print(int cv, int col);
	int	breaking()	{ return isbreaking; }
	void	setbreaking()	{ isbreaking++; }
};

class usrange : public nestrange {
  public:
	usrange()	{ }
	usrange(slug *p, stream *s) : nestrange(p, s) {}
	void dump() { printf("#### US	dv %d\n", height()); }
	range	*clone();
};

class ufrange : public nestrange {
	int	goalV, goal2;
  public:
	ufrange()	{ }
	ufrange(slug *p, stream *s) : nestrange(p, s) {
		goalV = p->parm; goal2 = p->parm2; }
	void 	dump() { printf("#### UF   dv %d goal %d goal2 %d\n",
		height(), goalV, goal2); }
	int	floatable()	{ return 1; }
	void	enqueue(int = 0);
	range	*clone();
	int	goal()		{ return goalV; }
	void	setgoal(int n)	{ goalV = goal2 = n; }
	void	pickgoal(int acv, double scale);
	void	restore()	{ goalV = first->parm; goal2 = first->ht; }
};

class bfrange : public nestrange {
	int	goalV, goal2;
  public:
	bfrange()	{ }
	bfrange(slug *p, stream *s) : nestrange(p, s) {
		goalV = p->parm; goal2 = p->parm2; }
	void 	dump() { printf("#### BF   dv %d goal %d goal2 %d\n",
		height(), goalV, goal2); }
	int	floatable()	{ return 1; }
	void	enqueue(int = 0);
	range	*clone();
	int	goal()		{ return goalV; }
	void	setgoal(int n)	{ goalV = goal2 = n; }
	void	pickgoal(int acv, double scale);
	void	restore()	{ goalV = first->parm; goal2 = first->parm2; }
	int	breakable()	{ return 1; }	// can be broken
};

class ptrange : public nestrange {
	int	pgno;
  public:
	int	pn()	{ return pgno; }
	ptrange(slug *p, stream *s) : nestrange(p, s) { pgno = p->parm; }
	void 	dump() { printf("#### PT   pgno %d dv %d\n", pgno, height()); }
};

class btrange : public nestrange {
	int	pgno;
  public:
	btrange(slug *p, stream *s) : nestrange(p, s) { pgno = p->parm; }
	void 	dump() { printf("#### BT   pgno %d dv %d\n", pgno, height()); }
};

// A stream is a sequence of ranges; we use this data structure a lot
// to traverse various sequences that crop up in page-making.
class stream {
  protected:
public:
	struct strblk {		// ranges are linked by these blocks
		strblk	*next;
		range	*rp;
	};
	strblk	*first;
	strblk	*last;
	strblk	*curr;
  public:
	stream()		{ curr = last = first = 0; }
	stream(range *r)	{ curr = last = first = new strblk;
					last->rp = r; last->next = 0; }
	void	freeall();	// note:  not a destructor
	void	dump();		// top level
	void	rdump();	// recursive
	int	restoreall();
	range	*current()	{ return curr->rp; }
	range	*next()		{ return curr && curr->next ? curr->next->rp : 0; }
	void	advance()	{ curr = curr->next; }
	range	*append(range *r);
	void	split();
	int	more()		{ return curr && curr->rp; }
	int	height();
	int	rawht();
};

// A generator iterates through all the ranges of a stream
// (not just the root ranges of nestranges).
class generator {
	stream	s;
	generator *child;
  public:
	generator()		{ child = 0; }
	generator(stream *sp)	{ s = *sp; child = 0; }
	range	*next();
};

extern stream	ptlist, btlist;		// page titles

#undef INFINITY
#define INFINITY 1000001

// A queue is a distinguished kind of stream.
// It keeps its contents in order by the serial numbers of the ranges.
// A queue can be blocked from dequeuing something to indicate
// that it's not worth considering the queue again on a given page.
class queue : public stream {
	strblk	*newguy;
  protected:
	int	blocked;
	void	check(char *);
  public:
	queue() : blocked(0)	{ }
	range	*enqueue(range *r);
	range	*dequeue();
	void	block()		{ blocked = 1; }
	void	unblock()	{ blocked = 0; }
	int	more()		{ return !blocked && stream::more(); }
	int	empty()		{ return !stream::more(); }
	int	serialno()	{ return empty() ? INFINITY : current()->serialno(); }
};

// functions in range.c
void checkout();
void startup(FILE *);
