# third pass:  setting lines and pages
# The input language of this pass is basically <width, word> pairs, where
# "word" may have imbedded strangeness (backspaces, etc.) for font changes
# and special characters.  Zero width is not special.  A third field may
# appear to indicate that this is a fragment of a word that can be
# hyphenated; the third field is the width of the hyphen that would have
# to be added if the line broke after this fragment.
# Negative widths denote special operations.  -3 is an error message, the
# second field being the message.  -1 and -2 are control messages to this
# pass, the difference being whether a break is implied or not.  The second
# field is a message type string; more fields may appear as arguments.  The
# semantics of control messages are often -- but not always! -- similar to
# those of troff commands.  For example, "linelen" is .ll, but "center" is
# not semantically equivalent to .ce -- it is related but more primitive.
BEGIN {
	# input and output details
	FS = "\t"
	nobreak = -1
	dobreak = -2
	message = -3
	errs = "awf.errs"	# default only, normally changed by "errsto"

	# page setup -- some are defaults only, normally altered by pass 2
	nextlineno = 1
	thispageno = 1
	topmargin = 5
	botmargin = 5
	ind = 0			# current indent
	tmpind = 0
	pageoffset = ""		# string to emit at start of each line
	nospacemode = 1
	hdrs["CH"] = "- % -"
	nop = split("LH,CH,RH,LF,CF,RF", hdrnames, ",")
	fph = 0			# print header on first page?
	fill = 1
	adj = "both"
	pagelen = 66
	linelen = 78

	# line-builder setup
	line = ""		# line so far, without padding
	paddable = ""		# x means corresp. char in "line" is paddable
	thislinelen = -1	# -1 means nothing there
	cont = " "		# thing to append to continue line
	contlen = 1
	eol = ""		# thing to append to break line
	eollen = 0
	padfrom = "R"		# "L" or "R", alternating for river avoidance

	# many spaces, so we can use substr to get any number we need
	sps = "                                                     "
	sps = sps sps sps sps sps sps sps sps sps sps
}
{
	# process word, if any, causing a break if appropriate
	if ($1 >= 0 && thislinelen < 0) {	# word, and first on line
		line = $2
		paddable = substr(sps, 1, length($2))
		thislinelen = $1
	} else if ($1 >= 0 && thislinelen+contlen+$1+$3 <= linelen-ind-tmpind) {
		# word, and it fits on line
		line = line cont $2
		if (cont == " ")
			paddable = paddable "x"
		else
			paddable = paddable substr(sps, 1, length(cont))
		paddable = paddable substr(sps, 1, length($2))
		thislinelen += contlen + $1
	} else if ($1 == nobreak || $1 == message)
		nop = 0		# no attention (i.e. break) needed here
	else if ($1 == dobreak && $2 == "need" && \
				nextlineno + $3 < pagelen + 1 - botmargin)
		nop = 0		# enough space is available, no action needed
	else if ($1 == dobreak && $2 == "toindent" && \
					ind + tmpind + thislinelen < ind) {
		# move to indent position within line; there is room
		n = ind - (ind + tmpind + thislinelen)
		line = line substr(sps, 1, n)
		# nothing before this is paddable
		paddable = substr(sps, 1, length(line))
		thislinelen += n
		# prevent padding immediately after this point
		cont = ""
		contlen = 0
	} else if (thislinelen >= 0 || ($1 == dobreak && $2 == "need")) {
		# must emit output, either due to break or "need"

		# if at top of page, header
		if (nextlineno == 1) {
			for (i = int((topmargin-1)/2); i > 0; i--) {
				print ""
				nextlineno++
			}
			for (hno in hdrnames) {
				h = hdrnames[hno]
				if (hdrs[h] ~ /%/) {
					n = split(hdrs[h], t, "%")
					thispagehdrs[h] = t[1] thispageno t[2]
				} else
					thispagehdrs[h] = hdrs[h]
			}
			if (fph || thispageno > 1) {
				lh = thispagehdrs["LH"]
				ch = thispagehdrs["CH"]
				rh = thispagehdrs["RH"]
				lsp = int((linelen - length(lh ch rh)) / 2)
				rsp = linelen - length(lh ch rh) - lsp
				print pageoffset lh substr(sps, 1, lsp) ch substr(sps, 1, rsp) rh
			} else
				print ""
			nextlineno++
			while (nextlineno <= topmargin) {
				print ""
				nextlineno++
			}
		}

		# the current line
		# first, add a trailing hyphen if any
		line = line eol
		paddable = paddable substr(sps, 1, length(eol))
		thislinelen += eollen

		# trim trailing spaces if any
		while (line ~ / $/) {
			line = substr(line, 1, length(line)-1)
			paddable = substr(paddable, 1, length(line))
			thislinelen--
		}

		# print it in a suitable way
		if (line == "")		# empty always prints as nothing
			print ""
		else if ($1 < 0 && $2 == "center") {
			# center it
			hsp = int((linelen - thislinelen) / 2)
			if (hsp < 0)
				hsp = 0
			print pageoffset substr(sps, 1, ind+tmpind+hsp) line
		} else if (adj == "left" || (adj == "both" && \
					($1 < 0 || index(paddable, "x") == 0)))
			# no right-margin adjustment (disabled, inappropriate
			# (line ended by break), or impossible)
			print pageoffset substr(sps, 1, ind+tmpind) line
		else if (adj == "both") {
			# hard case -- adjust right margin
			# sanity check
			if (length(paddable) != length(line))	# aieeeee
				printf "awf: %f != %f!\n", length(paddable), \
							length(line) >errs

			# compute parameters
			textlen = linelen - (ind+tmpind)
			mustadd = textlen - thislinelen
			npad = 0	# number of paddable spaces
			for (tmp = paddable; (i = index(tmp, "x")) > 0; \
							tmp = substr(tmp, i+1))
				npad++
			addatall = int(mustadd/npad)	# all grow this much
			spall = substr(sps, 1, addatall)
			nmore = mustadd - addatall*npad	# this many grow more

			# build padded output text
			out = substr(sps, 1, ind+tmpind)
			padno = 0
			while ((i = index(paddable, "x")) > 0) {
				out = out substr(line, 1, i)
				padno++
				out = out spall
				if (padfrom == "L") {
					if (padno <= nmore)
						out = out " "
				} else {
					if (padno > npad-nmore)
						out = out " "
				}
				line = substr(line, i+1)
				paddable = substr(paddable, i+1)
			}

			# print it, plus remnant not processed by loop
			print pageoffset out line

			# tidy up
			if (padfrom == "L")
				padfrom = "R"
			else
				padfrom = "L"
		}

		# tidy up after output line
		nextlineno++
		line = ""
		paddable = ""
		thislinelen = -1
		tmpind = 0
		nospacemode = 0

		# if we broke from a "need", go to bottom of page
		if ($1 == dobreak && $2 == "need")
			while (nextlineno < pagelen + 1 - botmargin) {
				print ""
				nextlineno++
			}

		# footer, if at bottom of page
		if (nextlineno >= pagelen + 1 - botmargin) {
			for (i = int((botmargin-1)/2); i > 0; i--) {
				print ""
				nextlineno++
			}
			# header code prepared thispagehdrs
			lf = thispagehdrs["LF"]
			cf = thispagehdrs["CF"]
			rf = thispagehdrs["RF"]
			lsp = int((linelen - length(lf cf rf)) / 2)
			rsp = linelen - length(lf cf rf) - lsp
			print pageoffset lf substr(sps, 1, lsp) cf substr(sps, 1, rsp) rf
			nextlineno++
			while (nextlineno <= pagelen) {
				print ""
				nextlineno++
			}
			nextlineno = 1
			thispageno++

			# after page break, should not space unnecessarily,
			# and should pad first line from right
			nospacemode = 1
			padfrom = "R"
		}

		# we are finally done with emitting output
		# pick up input word, if any
		if ($1 >= 0) {
			line = $2
			paddable = substr(sps, 1, length($2))
			thislinelen = $1
		}
	}

	# if we broke, next line should pad from right
	if ($1 == dobreak)
		padfrom = "R"

	# cleanup and post-break command processing
	if ($1 >= 0 || $2 == "nohyphen") {
		# reset hyphenation trickery after each word (fragment)
		cont = " "
		contlen = 1
		eol = ""
		eollen = 0
	} else if ($2 == "need" || $2 == "toindent")
		nop = 0		# dealt with above
	else if ($2 == "flush" || $2 == "center")
		nop = 0		# exist only to cause break
	else if ($1 == message)
		print "awf: " $2 >errs
	else if ($2 == "gap") {
		# gap between last word and next one should be >= $3
		if (thislinelen >= 0) {
			line = line substr(sps, 1, $3-1)
			paddable = paddable substr(sps, 1, $3-1)
			thislinelen += $3-1
		}
	} else if ($2 == "tabto") {
		# move to tab stop at $3
		if (thislinelen < 0)
			thislinelen = 0		# make line exist
		n = $3 - thislinelen
		if (n > 0) {			# must emit some space
			line = line substr(sps, 1, n)
			# nothing before a tab is paddable
			paddable = substr(sps, 1, length(line))
			thislinelen += n
			# suppress space following
			cont = ""
			contlen = 0
		}
	} else if ($2 == "errsto")
		errs = $3
	else if ($2 ~ /^[LCR][HF]$/)
		hdrs[$2] = $3
	else if ($2 == "fph")
		fph = $3
	else if ($2 == "space") {
		if (!nospacemode) {
			# generate an empty line, which will be flushed by
			# the next word; NB we know "space" caused a flush
			line = ""
			paddable = ""
			thislinelen = linelen + 1
			nospacemode = 0
		}
	} else if ($2 == "left")
		adj = "left"
	else if ($2 == "both")
		adj = "both"
	else if ($2 == "indent")
		ind = $3
	else if ($2 == "tempindent")
		tmpind = $3
	else if ($2 == "linelen")
		linelen = $3
	else if ($2 == "pagelen")
		pagelen = $3
	else if ($2 == "nospace")
		nospacemode = 1
	else if ($2 == "yesspace")
		nospacemode = 0
	else if ($2 == "hyphen") {
		# discretionary hyphen at this point
		cont = ""
		contlen = 0
		eol = $3
		eollen = $4
	} else if ($2 == "userhyphen") {
		# user-supplied hyphen at this point
		cont = $3
		contlen = $4
		eol = $3
		eollen = $4
	} else if ($2 == "pageoffset")
		pageoffset = substr(sps, 1, $3)
	else
		print "awf: URK -- INTERNAL OPCODE `" $2 "' UNKNOWN" >errs
}
END {
	# second pass is supposed to fake a .ne to flush the last page
	if (nextlineno != 1)
		print "awf: last page not flushed!" >errs
}
