-- TAGraphicsHacksImpl.mesa
-- Rick Beach, June 30, 1982 10:15 pm
-- Maureen Stone October 22, 1982 3:01 pm

DIRECTORY
	CGArea USING [Empty, Ref, Remove],
	CGContext USING [Rep],
	CGDevice USING [Ref, Rep],
	CGMatrix USING [Make, Ref],
	CGSource USING [Ref],
	CGStorage USING [qZone],
	Graphics USING [Box, Context, NewContext],
	GraphicsBasic USING [Trap],
	JaMFnsDefs USING [PushReal, Register],
	TJaMGraphics USING [Painter],
	JaMOps USING [defaultFrame],
	Real,
	TAGraphicsHacks,
	TJaMGraphicsPrivate USING [EstablishNonViewerContext, ForgetNonViewerContext];

TAGraphicsHacksImpl: PROGRAM
	IMPORTS CGArea, CGMatrix, CGStorage, Graphics, JaMFnsDefs, TJaMGraphics, JaMOps, TJaMGraphicsPrivate
	EXPORTS GraphicsBasic, TAGraphicsHacks = {

-- convince the world that I know what is in these opaque types
DeviceObject: PUBLIC TYPE = CGDevice.Rep;
ContextObject: PUBLIC TYPE = CGContext.Rep;

-- a global bounds box 
boundsBox: Graphics.Box;
nullBoundsBox: Graphics.Box = [Real.LargestNumber, Real.LargestNumber, -Real.LargestNumber, -Real.LargestNumber];

Data: TYPE = REF DataRep;
DataRep: TYPE = RECORD [
  matrix: CGMatrix.Ref ← NIL -- base transformation matrix
  ];
dataZone: ZONE = CGStorage.qZone;
repZone: ZONE = CGStorage.qZone;

NewBoxerDevice: PUBLIC PROCEDURE RETURNS[CGDevice.Ref] = {
	data: Data ← dataZone.NEW[DataRep];
	data.matrix ← CGMatrix.Make[[1,0,0,1,0,0]];
	boundsBox ← nullBoundsBox;
	RETURN[repZone.NEW[CGDevice.Rep ← [
	GetMatrix: GetMatrix, GetBounds: GetBounds, Show: BoxerShow,
    GetRaster: BoxerGetRaster, data: data]]];
	};

GetMatrix: SAFE PROC[self: CGDevice.Ref] RETURNS[CGMatrix.Ref] = TRUSTED {
  data: Data ← NARROW[self.data];
  RETURN[data.matrix];
  };

GetBounds: SAFE PROC[self: CGDevice.Ref] RETURNS[Graphics.Box] = TRUSTED {
	RETURN[[0.1, 0.1, 4000-.1, 4000-.1]]};

BoxerShow: SAFE PROC[self: CGDevice.Ref, area: CGArea.Ref, src: CGSource.Ref, map: CGMatrix.Ref] = TRUSTED {
	-- extract the bounding box from the area and update the context bbox
	UNTIL CGArea.Empty[area] DO
		trap: GraphicsBasic.Trap ← CGArea.Remove[area];
    	{	OPEN boundsBox, trap;
			xmin ← MIN[xmin, xbotL];
			xmin ← MIN[xmin, xtopL];
			ymin ← MIN[ymin, ybot];
			xmax ← MAX[xmax, xbotR];
			xmax ← MAX[xmax, xtopR];
			ymax ← MAX[ymax, ytop]};
		ENDLOOP;
	};

BoxerGetRaster: SAFE PROC[self: CGDevice.Ref] RETURNS[LONG POINTER,CARDINAL] = TRUSTED {
	-- no raster for this device (sic!)
	RETURN[NIL,0];
	};

JBeginBox: PROCEDURE[] = {
	-- JaM callable procedure to begin bounding boxing
	context: Graphics.Context ← Graphics.NewContext[NewBoxerDevice[]];
	TJaMGraphicsPrivate.EstablishNonViewerContext[JaMOps.defaultFrame, context];
	};

JEndBox: PROCEDURE[] = {
	-- JaM callable procedure to return the bounding box
	paint: PROC [context: Graphics.Context] = {box ← EndBox[context]};
	box: Graphics.Box;
	TJaMGraphics.Painter[paint];
	TJaMGraphicsPrivate.ForgetNonViewerContext[JaMOps.defaultFrame];
	JaMFnsDefs.PushReal[box.xmin];
	JaMFnsDefs.PushReal[box.ymin];
	JaMFnsDefs.PushReal[box.xmax];
	JaMFnsDefs.PushReal[box.ymax];
	};

EndBox: PUBLIC PROCEDURE[self: Graphics.Context] RETURNS[Graphics.Box] = {
	-- reset the context device pointer and return the bounding box on the JaM stack
	IF boundsBox=nullBoundsBox THEN RETURN[[0,0,0,0]]
	ELSE RETURN[boundsBox] };

JaMFnsDefs.Register[".beginbox", JBeginBox];
JaMFnsDefs.Register[".endbox", JEndBox];

}.