// P R E B A N D -- Band management for prescan
//   errors 1200
//
//BandWriteInit()
//	Called at beginning and end of prescan phase.
//BandWriteClose()
//	Close out after all pages written
//BandWriteBeginPage()
//	Call when beginning a new PRESS page.
//BandWriteEndPage(pg)
//	Call when ending a PRESS page.  The argument is a PageG
//	structure that specifies some things about the page (FirstBand
//	and LastBand must be filled in; BandPos will be filled in).
//BandWrite(s,n)
//	Write n words from vector BandFree (BandFree!0, !1... !n-1) into
//	the band corresponding to scan-line s.  Put out copy synch or
//	regular synch if necessary.
//BandSync(color)
//	Record a sync point in the processing of band data.
//BandOnCopy(n)
//	Called to say that following stuff is to appear only on copy n.
//	n=0 means on all copies.
//
//Needs description of how it works.....
//

get "PressInternals.df"
get "PressParams.df"

// outgoing procedures
external
	[
	BandWriteInit
	BandWriteClose
	BandWriteBeginPage
	BandWriteEndPage
	BandWrite
	BandSync
	BandOnCopy
	BandRecords
	]

// outgoing statics
external
	[
	BandFree
	BandWindow		//Window on band file (needed for font load)
	BandCurHue;BandCurSat;BandCurBright
	]
static
	[
	BandFree
	BandWindow		//Window on band file
	BandCurHue;BandCurSat;BandCurBright
	]

// incoming procedures
external
	[
//WINDOW
	WindowInit
	WindowClose
	WindowFlush
	WindowGetPosition
	WindowSetPosition
	WindowRead
	WindowWrite
	WindowReadBlock
	WindowWriteBlock
	WindowNext

//PRESCAN
	PreFSGet

//PRESS
	PressError
	FSGetX
	FSPut

//PRESSML
	Ugt
	DoubleAdd
	DoubleAddV
	DoubleCop
	DoubleSub
	DivFull

//OS
	MoveBlock
	Zero

//METER
	MeterBlock

//CURSOR
	CursorToggle
	]

// incoming statics
external
	[
	DPzero			//Double precision zero.
	BandFile		//File for bands.
	nBands		//Number of bands required for this device
	BESizes
	PSStats			//Statistics
	Report
	nLeadingBands;nVisibleBands;nTrailingBands
	nPrinterColors
	Transparent	//allow sync codes to wrap around
	]

// internal statics
static
	[
	BandIndex		//Table of band header pointers
	BandCopyT		//Table of copy info for each band
	BandSyncT		//Sync number for each band
	BandBuf			//Pointer to in core portion of bands
	BandPos			//File position of bands for this page
	BandSegments		//Number of "dumps" on disk

	BandCurCopy		//Current OnCopy number
	BandCurSync		//Current sync count
	]

// File-wide structure and manifest declarations.

// Procedures


let BandWriteInit() be
 [
   compileif BEMaxSize ge BANDInCoreSize then [ foo=0 ]

   BandBuf=FSGetX(BANDInCoreSize)
   BandIndex=FSGetX(nBands)
   BandCopyT=FSGetX(nBands)
   BandSyncT=FSGetX(nBands)
   BandWindow=WindowInit(BandFile,2)
   BandPos=FSGetX(nBandSegmentsMax*2+2)
 ]

and BandWriteClose() be
 [	FSPut(BandBuf)
	FSPut(BandIndex)
	FSPut(BandCopyT)
	FSPut(BandSyncT)
//WindowClose(BandWindow) now called from PreScanClose
 ]

and BandWriteBeginPage() be
 [
   BandSegments=0		//None on the disk.
   BandFree=BandBuf+1		//Place for user to put entry
   Zero(BandCopyT,nBands)
   Zero(BandSyncT,nBands)
   Zero(BandIndex,nBands)
   BandCurCopy=0
   BandCurSync=0
   BandCurHue=0
   BandCurSat=0
   BandCurBright=0
 ]

and BandWriteEndPage(pg) be
 [
   if BandSegments eq 0 then	//Can write it out directly
     [
      BandFlush(pg)
      return
     ]
   BandFlush(0)			//Go handle current buffer.

//Now merge all the segments.
// There are BandSegments segments to merge; BandPos!(segment*2) is
// the double-word file position of the beginning of the segments.  Bear
// in mind that initially, all segments have bands running from 0 to nBANDS-1.
//

   let numSegs=BandSegments
   let delta=pg>>PageG.FirstBand	//Amount to pass up.
   for s=0 to numSegs-1 do //ignore initial empty bands
	DoubleAddV(BandPos+s*2, delta)

[MergeLoop
   if numSegs le nMaxMergeInputs then
		[   //only one merge step is needed now
		BandMergeStep(pg, 0, numSegs-1, true)
		break
		]
   //we will merge nMaxMergeInputs at a time
   let breadth=(numSegs+nMaxMergeInputs-1)/nMaxMergeInputs
   for c=0 to breadth-2 do
     [
     let l=c*nMaxMergeInputs
     let r=l+nMaxMergeInputs-1
     BandMergeStep(pg,l,r,false)
     ]
   //the final merge on this level has a funny right end point
   BandMergeStep(pg, (breadth-1)*nMaxMergeInputs, numSegs-1, false) 
   //now, compact the answers down to the low end of BandPos array
   for i=1 to breadth-1 do
       DoubleCop(BandPos+i*2,BandPos+i*nMaxMergeInputs*2)
   numSegs=breadth
]MergeLoop repeat

   //insert two empty band commands
   for i=1 to 2 do WindowWriteBlock(BandWindow,DPzero,2)
   WindowNext(BandWindow)

   //and, if it's a graphic page, add padding+1 page for nextBandFreeList
   unless pg>>PageG.SimplePage do WindowNext(BandWindow)

   DoubleCop(lv pg>>PageG.BandPos, BandPos)	//1 segment => done
   let endPos=vec 1
   WindowGetPosition(BandWindow,endPos)
   DoubleSub(endPos,BandPos)
   pg>>PageG.nRecords=DivFull(endPos,BandWindow>>W.BufSize)
   pg>>PageG.ColorPass=0
   for i=2 to nPrinterColors do 
	[ CopyPage(pg,pg+(size PageG/16));pg=pg+(size PageG/16)
	]
 ]

and BandMergeStep(pg, firstSeg, lastSeg, finalMerge) be
//merge the band segments from first to last, storing the result
// back in the firstSeg slot.  Don't put length headers on if the
// finalMerge flag is true.
[
   let numToMerge=lastSeg-firstSeg+1
   if numToMerge gr nMaxMergeInputs then PressError(1206)
   let winVec=vec nMaxMergeInputs
   let lenVec=vec nMaxMergeInputs
   WindowFlush(BandWindow)
   let rp=vec 1
   WindowGetPosition(BandWindow, rp)

   for s=firstSeg to lastSeg do
	[
	let a=PreFSGet(1024+30)	//Hack to ensure enough core avail.
	FSPut(a)
    let w=WindowInit(BandWindow>>W.File ,1)
	winVec!(s-firstSeg)=w
	WindowSetPosition(w,BandPos+s*2)
	]

   let v=vec 500			//Buffer for copying
   for b=pg>>PageG.FirstBand to pg>>PageG.LastBand do
	[BandLoop
	CursorToggle(2)
	let len=0
	for s=0 to numToMerge-1 do
	   [
	   let l=WindowRead(winVec!s)
	   len=len+l
	   lenVec!s=l
	   ]
	unless finalMerge then WindowWrite(BandWindow, len)

	for s=0 to numToMerge-1 do
	   [
	   let l=lenVec!s
	   while l ne 0 do
		[
		let tl=l
		if Ugt(tl,500) then tl=500
		WindowReadBlock(winVec!s, v, tl)
		WindowWriteBlock(BandWindow, v, tl)
		l=l-tl
		]
	   ]

	if finalMerge then
	   [ unless pg>>PageG.SimplePage then //bogus char for conversion
		WindowWriteBlock(BandWindow,table [ #100000;0],2)
	   WindowWriteBlock(BandWindow,DPzero,2)	//End code = [0;0] (ORbit).
	   compileif MeterSw then
		[
		len=(len+1)/BandDistUnit
		let adr=(lv PSStats>>PSStat.BandSizes)+
			((len le BandDist-2 )? len,BandDist-1)
		@adr=@adr+1
		]
	   ]
	]BandLoop
   for s=0 to numToMerge-1 do WindowClose(winVec!s)

   DoubleCop(BandPos+firstSeg*2, rp)	//filePos of resulting merged segment
]


and BandFlush(pg) be
 [
   compileif MeterSw then [ MeterBlock(METERBandFlush) ]
   compileif ReportSw then
	[
	if pg eq 0 then Report>>REP.BandFileSorts=Report>>REP.BandFileSorts+1
	]
   let fband=0
   let lband=nBands-1
   test pg then
     [
      fband=pg>>PageG.FirstBand
      lband=pg>>PageG.LastBand
      WindowGetPosition(BandWindow,lv pg>>PageG.BandPos)
     ] or
     [
      WindowGetPosition(BandWindow,BandPos+BandSegments*2)
      BandSegments=BandSegments+1
      if BandSegments eq nBandSegmentsMax then PressError(1200)
     ]

   compileif DebugSw then
     [
     if fband ls 0 % lband ge nBands then PressError(1202)
     ]

   for b=fband to lband do
     [
      CursorToggle(2)
//Reverse the list of entries for this band and count length
      let len=0
      let prev=0
      let p=BandIndex!b
      while p do
	[
	compileif DebugSw then
	   [
	   if (p!1&CharBit) eq 0 & p!1 gr BEMaxH then PressError(1203)
	   if Ugt(BandBuf, p) % Ugt(p, BandBuf+BANDInCoreSize-1) then PressError(1204)
	   ]
	let l=size BEChar/16
	if (p!1&CharBit) eq 0 then l=BESizes!(p!1)
	len=len+l
	let next=p!0
	p!0=prev
	prev=p
	p=next
	]

//Record lengths:
	compileif (size BEEnd/16) ne 2 then [ foo=nil]
	compileif ReportSw then	//account for BEEnd
	  [ DoubleAddV(lv Report>>REP.BandSize, len+2)
	  ]

//Record length distributions if final file:
	compileif MeterSw then
	[
	let adr=(lv PSStats>>PSStat.BandSizes)+
		((len le BandDist-2 )? len,BandDist-1)
	if pg then @adr=@adr+1
	]

//Now write out length (only if intermediate file)
	unless pg then WindowWrite(BandWindow,len)

//Now write band entries -- open code for speed
	let off=BandWindow>>W.Offset

	while prev do
	[
	let l=size BEChar/16
	if (prev!1&CharBit) eq 0 then l=BESizes!(prev!1)
	test -off ge l
	   ifso [ MoveBlock(BandWindow>>W.Base+off, prev+1, l)
			off=off+l ]
	   ifnot [ BandWindow>>W.Offset=off
			WindowWriteBlock(BandWindow,prev+1,l)
			off=BandWindow>>W.Offset ]
	prev=prev!0
	]

	BandWindow>>W.Offset=off
//Now write end code (only if final file)
	if pg then 
	[ unless pg>>PageG.SimplePage do	//bogus char for conversion
		 WindowWriteBlock(BandWindow,table [ #100000;0],2)
	WindowWriteBlock(BandWindow,DPzero,2)
	]
     ]

//Now reset band buffer stuff
   BandFree=BandBuf+1
   Zero(BandIndex,nBands)
   if pg eq 0 then return	//just an intermediate stage

   //insert two empty band commands 
   for i=1 to 2 do WindowWriteBlock(BandWindow,DPzero,2)
   WindowNext(BandWindow)
   unless pg>>PageG.SimplePage do WindowNext(BandWindow)

//and record nRecords
   let endPos=vec 1
   WindowGetPosition(BandWindow,endPos)
   DoubleSub(endPos,lv pg>>PageG.BandPos)
   pg>>PageG.nRecords=DivFull(endPos,BandWindow>>W.BufSize)
   pg>>PageG.ColorPass=0
   for i=2 to nPrinterColors do 
	[ CopyPage(pg,pg+(size PageG/16));pg=pg+(size PageG/16)
	]
 ]

and CopyPage(inPage,outPage) be
 [ MoveBlock(outPage,inPage,size PageG/16)
   outPage>>PageG.ColorPass=inPage>>PageG.ColorPass+1
   unless inPage>>PageG.ColorUsed then return	//identical!!

   WindowGetPosition(BandWindow,lv outPage>>PageG.BandPos)
   let inPos=vec 1;MoveBlock(inPos,lv inPage>>PageG.BandPos,2)

   //kludge for funny page counts:
   let evenBoundary=((inPos!1)&(BandWindow>>W.BufSize-1)) eq 0
   unless evenBoundary do outPage>>PageG.nRecords=outPage>>PageG.nRecords+1

   let outPos=vec 1;MoveBlock(outPos,lv outPage>>PageG.BandPos,2)
   let v=FSGetX(BandWindow>>W.BufSize)
   for i=1 to outPage>>PageG.nRecords do
    [ WindowSetPosition(BandWindow,inPos)
      WindowReadBlock(BandWindow,v,BandWindow>>W.BufSize)
      WindowSetPosition(BandWindow,outPos)
      WindowWriteBlock(BandWindow,v,BandWindow>>W.BufSize)
      DoubleAddV(inPos,BandWindow>>W.BufSize)
      DoubleAddV(outPos,BandWindow>>W.BufSize)
    ]
   FSPut(v)
   unless evenBoundary do WindowNext(BandWindow)
 ]

and BandRecords(n)=valof
 [ let r=BandFile>>F.Pagesize
   resultis (n+r-1) rshift BandFile>>F.LogPagesize
 ]

and BandSync(hue,sat,bright) be
 [ let change=false
   if hue ne BandCurHue then [ BandCurHue=hue;change=true]
   if sat ne BandCurSat then [ BandCurSat=sat;change=true]
   if bright ne BandCurBright then [ BandCurBright=bright;change=true]
   if change then BandCurSync=BandCurSync+1
   if BandCurSync eq syncInfinity then 
	test Transparent
	  ifso   // let sync's wrap around
		[ BandCurSync=1; Zero(BandSyncT, nBands) ] 
	  ifnot PressError(1205)
 ]

and BandOnCopy(c) be
 [
   BandCurCopy=c
 ]

and BandWrite(s,n) be
 [
   let b=s rshift LogBANDWidth		//Band number
   if s ls 0 % b ge nBands then [ PressError(1201); return ]

   if BandCopyT!b ne BandCurCopy then
    [				//Need to put out copy code.
      BandCopyT!b=BandCurCopy
      let save=vec BEMaxSize
      MoveBlock(save,BandFree,BEMaxSize)
      BandFree>>BECopy.H=BECopyH
      BandFree>>BECopy.Copy=BandCurCopy
      BandWrite(s,size BECopy/16)
      MoveBlock(BandFree,save,BEMaxSize)
    ]

   if BandSyncT!b ne BandCurSync then
    [				//Need to put out sync code.
      BandSyncT!b=BandCurSync
      let save=vec BEMaxSize
      MoveBlock(save,BandFree,BEMaxSize)
      BandFree>>BESync.H=BESyncH
      BandFree>>BESync.Sync=BandCurSync
      BandFree>>BESync.Hue=BandCurHue
      BandFree>>BESync.Saturation=BandCurSat
      BandFree>>BESync.Intensity=BandCurBright
      BandWrite(s,size BESync/16)
      MoveBlock(BandFree,save,BEMaxSize)
    ]

//Now can actually write n words into band b.  The word before BandFree
// is available for a pointer (amazing!)


   BandFree!-1=BandIndex!b		//Link into list for this band
   BandIndex!b=BandFree-1
   BandFree=BandFree+n+1

   if Ugt(BandFree,BandBuf+(BANDInCoreSize-BEMaxSize-2)) then
	BandFlush(0)			//Full -- flush and reset

]