/*
 * (C) 1990 by Adobe Systems Incorporated. All rights reserved.
 *
 * This file may be freely copied and redistributed as long as:
 *   1) This entire notice continues to be included in the file, 
 *   2) If the file has been modified in any way, a notice of such
 *      modification is conspicuously indicated.
 *
 * PostScript, Display PostScript, and Adobe are registered trademarks of
 * Adobe Systems Incorporated.
 * 
 * ************************************************************************
 * THE INFORMATION BELOW IS FURNISHED AS IS, IS SUBJECT TO CHANGE WITHOUT
 * NOTICE, AND SHOULD NOT BE CONSTRUED AS A COMMITMENT BY ADOBE SYSTEMS
 * INCORPORATED. ADOBE SYSTEMS INCORPORATED ASSUMES NO RESPONSIBILITY OR 
 * LIABILITY FOR ANY ERRORS OR INACCURACIES, MAKES NO WARRANTY OF ANY 
 * KIND (EXPRESS, IMPLIED OR STATUTORY) WITH RESPECT TO THIS INFORMATION, 
 * AND EXPRESSLY DISCLAIMS ANY AND ALL WARRANTIES OF MERCHANTABILITY, 
 * FITNESS FOR PARTICULAR PURPOSES AND NONINFINGEMENT OF THIRD PARTY RIGHTS.
 * ************************************************************************
 */

/*
 *	ControlView.m
 */

#import "ControlPoint.h"
#import "ControlView.h"
#import "ControlViewWraps.h"
#import <appkit/Bitmap.h>
#import <appkit/Button.h>
#import <appkit/Control.h>
#import <appkit/Matrix.h>
#import <appkit/TextField.h>
#import <dpsclient/wraps.h>

@implementation ControlView

+newFrame:(NXRect *) frm
{
	float		ViewRect[4];
	
	self = [super newFrame:frm]; 
	[self allocateGState]; 

	NX_MALLOC(XYPoints, float, MAX_ARRAY);
	NX_MALLOC(OpsBuffer, char, MAX_UPATHOPS); 
	NX_MALLOC(XYBuffer, float, (MAX_UPATHPTS));

	ViewRect[0] = ViewRect[1] = 0.0;
	ViewRect[2] = bounds.size.width;
	ViewRect[3] = bounds.size.height;
	PSWDefsView (BGCOLOR, BGCOLORSTR, BGWIDTHSTR, ViewRect);
	
	srand(1);
	indexOfPoints = 0;

	bitMap = [[Bitmap newSize:FIGURESIZE :FIGURESIZE
				type:NX_UNIQUEBITMAP] setFlip:NO];

	[self	makePoints:self];
	
	return self;
}

/*
 * Free the buffers
 */ 
- free
{
	if (OpsBuffer)
		NX_FREE(OpsBuffer);
	if (XYPoints)
		NX_FREE(XYPoints);
	if (XYBuffer)
		NX_FREE(XYBuffer);
    
	return [super free];
}

/* The following methods are used to obtain the ids of the appropriate 
 * text fields.  They were created by interface builder.
 */
-setDrawMethodsMatrix:anObject
{
	drawMethodsMatrix = anObject;
	return self;
}

-setDisplayTimesMatrix:anObject
{
	displayTimesMatrix = anObject;
	return self;
}

-setNumberPointsMatrix:anObject
{
	numberPointsMatrix = anObject;
	numberOfPoints = [numberPointsMatrix selectedTag];
	return self;
}

-setControlPoint:anObject
{
	controlPoint = anObject;
	return self;
}

-setButtonTitle:(OP) title
{
	if (title == FILL)
	{
		[[drawMethodsMatrix cellAt:2 :0] setTitle:"ufill"];
		[[drawMethodsMatrix cellAt:3 :0]setTitle:"rectfill"];
	}
	else
	{
		[[drawMethodsMatrix cellAt:2 :0] setTitle:"ustroke"];
		[[drawMethodsMatrix cellAt:3 :0]setTitle:"rectstroke"];
	}

	return self;
}

-setButtonEnable:(BOOL) enable
{
	[[drawMethodsMatrix cellAt:3 :0] setEnabled:enable];

	return self;
}

-psTrace:sender
{
	if (PSTrace == NO)
		[[sender selectedCell] setTitle:"PS Trace On"];
	else
		[[sender selectedCell] setTitle:"PS Trace Off"];

	PSTrace = !PSTrace;
	
	return self;
}

-configureDependence:sender
{
	[controlPoint  deviceDependence:[sender state]];
	[self  eraseTimes:self];

	return self;
}

-configurePoints:sender
{
	
	numberOfPoints = [numberPointsMatrix selectedTag];
	
	/* Choose new starting index to instill a little change in the display. */
	indexOfPoints = (rand () % (MAX_POINTS - numberOfPoints + 1)) * 2;

	[self eraseTimes:self];
	return self;
}

/*
 * This method fills up the array of points randomly. If the number of 
 * points to draw is different from the previous time, then a new place 
 * in the array will be selected for the points (except for the case of 
 * the largest number of points, in this instance the 0 index will be used). 
 * This random selection of indices introduces a little bit of change in 
 * the display.
 */
-makePoints:sender
{
	int  		i;
	
	for (i = 0; i < MAX_ARRAY; i = i +2)
	{
		XYPoints[i] = 
		    (rand () % (long)((bounds.size.width -8) * 100) + 400) * 0.01;
		XYPoints[i+1] = 
		    (rand () % (long)((bounds.size.height -8) * 100) + 400) * 0.01;
	}

	return self;
}

-eraseTimes:sender
{
	int		i;
	
	for (i = 0; i < [displayTimesMatrix cellCount]; i++)
		[[displayTimesMatrix cellAt:i :0] setStringValue:""];

	return self;
}

-drawViewOne:sender
{
	int	i, row;
	
	row = [sender selectedRow];
		drawFlags.field = 0x80 >> row;
	
	[self display];

	return self;
}

-drawViewAll:sender
{
	drawFlags.field = DRAWALL;

	[self display];

	return self;
}


/*
 * Below are six methods that use different approaches to drawing control 
 * points.
 */

/* The drawing will center around the point passed in.*/
-drawBasic:(int) cell
{
	int		ElapsedTime, i;
	
	char		*basicProc, *basicOp;
	
	basicProc = [controlPoint  getBasicProc];
	basicOp = [controlPoint  getBasicOp];

	[[displayTimesMatrix cellAt:cell :0] setStringValue:""];
		PSWEraseView();
		PSsetlinewidth(0);
		PSWMarkTime(); NXPing();	
			if (PSTrace)
				DPSTraceContext(DPSGetCurrentContext(), YES);

			for (i = indexOfPoints;
			     i < indexOfPoints + (numberOfPoints*2);
			     i = i+2)
				PSWBasic(XYPoints[i], XYPoints[i+1], basicProc, basicOp);

			if (PSTrace)
				DPSTraceContext(DPSGetCurrentContext(), NO);
		PSWReturnTime (&ElapsedTime);
	[[displayTimesMatrix cellAt:cell :0] setIntValue:ElapsedTime];

	return self;
}

/*
 * Cache a single user path. Translate to each location and then
 * image the user path.
 */
-drawUserCache:(int) cell
{
	int 		ElapsedTime, i, i_pt, i_op, j;
	
	char		*userOp, *userOpsArray;
	
	float		*userPtsArray; 

	userPtsArray = [controlPoint  getUserPtsArray];
	userOpsArray = [controlPoint  getUserOpsArray];
	userOp = [controlPoint  getUserOp];
	
	[[displayTimesMatrix cellAt:cell :0] setStringValue:""];
		PSWEraseView();
		PSsetlinewidth(0.15);
		PSWMarkTime ();  NXPing();
			i_op = i_pt = 0;
			OpsBuffer[i_op++] = dps_ucache;
			
			XYBuffer[i_pt++] = -FIGURESIZE/2;
			XYBuffer[i_pt++] = -FIGURESIZE/2;
			XYBuffer[i_pt++] = FIGURESIZE/2;
			XYBuffer[i_pt++] = FIGURESIZE/2;
			OpsBuffer[i_op++] = dps_setbbox;
		
			XYBuffer[i_pt++] = 0;
			XYBuffer[i_pt++] = 0;
			OpsBuffer[i_op++] = dps_moveto;

			for (j = 1; j <= userPtsArray[0]; j++)
				XYBuffer[i_pt++] = userPtsArray[j];

			for (j = 1; j <= (int) userOpsArray[0]; j++)
				OpsBuffer[i_op++] = userOpsArray[j];

			PSgsave();
			if (PSTrace)
				DPSTraceContext(DPSGetCurrentContext(), YES);

			PStranslate(XYPoints[indexOfPoints], XYPoints[indexOfPoints+1]);
			for (i = indexOfPoints;
			     i < indexOfPoints + (numberOfPoints*2);
			     i = i+2)
			{
				PSWUserPath(XYBuffer, i_pt, OpsBuffer, i_op, userOp);
				PStranslate(
				  XYPoints[i+2] - XYPoints[i], XYPoints[i+3] - XYPoints[i+1]);
			}

			if (PSTrace)
				DPSTraceContext(DPSGetCurrentContext(), NO);
			PSgrestore();
		PSWReturnTime (&ElapsedTime);
	[[displayTimesMatrix cellAt:cell :0] setIntValue:ElapsedTime];

	return self;
}

/* Represents the control points as a large user path comprised of a 
 * set of sub user paths.
 */
-drawUserPath:(int) cell
{
	int 		ElapsedTime, i,  i_pt, i_op, j;
	
	char		*userOp, *userOpsArray;
	
	float		*userPtsArray; 

	userPtsArray = [controlPoint  getUserPtsArray];
	userOpsArray = [controlPoint  getUserOpsArray];
	userOp = [controlPoint  getUserOp];
	
	[[displayTimesMatrix cellAt:cell :0] setStringValue:""];
		PSWEraseView();
		PSsetlinewidth(0);
		PSWMarkTime ();  NXPing();
			XYBuffer[0] = bounds.origin.x;
			XYBuffer[1] = bounds.origin.y;
			XYBuffer[2] = bounds.origin.x + bounds.size.width;
			XYBuffer[3] = bounds.origin.y + bounds.size.height;
			OpsBuffer[0] = dps_setbbox;
		
			if (PSTrace)
				DPSTraceContext(DPSGetCurrentContext(), YES);

			i = 0; i_pt = 4; i_op = 1;
			while (i < numberOfPoints * 2)
			{
				if ((i_pt + userPtsArray[0] > MAX_UPATHPTS) ||
				    (i_op + (int) userOpsArray[0] > MAX_UPATHOPS))
				{
					PSWUserPath(XYBuffer, i_pt, OpsBuffer, i_op, userOp);
					i_pt = 4; i_op = 1;
				}
			
				XYBuffer[i_pt++] = XYPoints[indexOfPoints + i++];
				XYBuffer[i_pt++] = XYPoints[indexOfPoints + i++];
				OpsBuffer[i_op++] = dps_moveto;

				for (j = 1; j <= userPtsArray[0]; j++, i_pt++)
					XYBuffer[i_pt] = userPtsArray[j];
				
				for (j = 1; j <= (int) userOpsArray[0]; j++, i_op++)
					OpsBuffer[i_op] = userOpsArray[j];
			}
			PSWUserPath(XYBuffer, i_pt, OpsBuffer, i_op, userOp);

			if (PSTrace)
				DPSTraceContext(DPSGetCurrentContext(), NO);
		PSWReturnTime (&ElapsedTime);
	[[displayTimesMatrix cellAt:cell :0] setIntValue:ElapsedTime];

	return self;
}

/*
 * We have to calculate the offset from the center because where we 
 * say draw, rectfill draws.
 */
-drawRectOp:(int) cell
{
	int		ElapsedTime, i, j;
	
	char		*rectOp;
	
	rectOp = [controlPoint  getRectOp];
	
	[[displayTimesMatrix cellAt:cell :0] setStringValue:""];
		PSWEraseView();
		PSsetlinewidth(0);
		PSWMarkTime (); NXPing();		
			if (PSTrace)
				DPSTraceContext(DPSGetCurrentContext(), YES);

			for (i = indexOfPoints, j = 0;
			     i < indexOfPoints + (numberOfPoints*2);
			     i = i+2, j = j+4)
			{
				if (j+3 >  MAX_RECTPTS)
				{
					PSWRectDraw (XYBuffer, j, rectOp);
					j = 0;
				}
				XYBuffer[j] = XYPoints[i] - RECTOFFSET;
				XYBuffer[j+1] = XYPoints[i+1] - RECTOFFSET;
				XYBuffer[j+2] = RECTSIZE;
				XYBuffer[j+3] = RECTSIZE;
			}
			PSWRectDraw (XYBuffer, j, rectOp);

			if (PSTrace)
				DPSTraceContext(DPSGetCurrentContext(), NO);
		PSWReturnTime (&ElapsedTime);
	[[displayTimesMatrix cellAt:cell :0] setIntValue:ElapsedTime];

	return self;
}	

/*
 * Draws the control point into the bitmap and then composites it at each
 * control point location.
 */
-drawComposite:(int) cell
{
	int			ElapsedTime, i;

	NXPoint		point;

	[controlPoint drawBitMap:bitMap];
	[[displayTimesMatrix cellAt:cell :0] setStringValue:""];
		PSWEraseView();
		PSWMarkTime (); NXPing();
			if (PSTrace)
				DPSTraceContext(DPSGetCurrentContext(), YES);

			for (i = indexOfPoints;
			     i < indexOfPoints + (numberOfPoints*2);
			     i = i+2)
			{
				point.x = XYPoints[i] - FIGURESIZE/2;
				point.y = XYPoints[i+1] - FIGURESIZE/2;
				[bitMap composite:NX_SOVER toPoint:&point];
			}

			if (PSTrace)
				DPSTraceContext(DPSGetCurrentContext(), NO);
		PSWReturnTime (&ElapsedTime);
	[[displayTimesMatrix cellAt:cell :0] setIntValue:ElapsedTime];

	return self;
}

/*
 * Performs a moveto show for each control point.
 */
-drawShow:(int) cell
{
	int		ElapsedTime, i;

	char		fontchar[2];

	fontchar[0] = [controlPoint getChar];
	fontchar[1] = 0;
	[controlPoint selectFont:FONTSIZE];

	[[displayTimesMatrix cellAt:cell :0] setStringValue:""];
	    PSgsave();
		PSWEraseView();
		PSWMarkTime (); NXPing();
			if (PSTrace)
				DPSTraceContext(DPSGetCurrentContext(), YES);

			for (i = indexOfPoints;
			     i < indexOfPoints + (numberOfPoints*2);
			     i = i+2)
				PSWShow(XYPoints[i], XYPoints[i+1], fontchar);		

			if (PSTrace)
				DPSTraceContext(DPSGetCurrentContext(), NO);
		PSWReturnTime (&ElapsedTime);
	    PSgrestore();
	[[displayTimesMatrix cellAt:cell :0] setIntValue:ElapsedTime];

	return self;
}

/*
 * Places the characters and relative offsets into arrays and executes
 * a single xyshow.
 */
-drawXYShow:(int) cell
{
	int		ElapsedTime, i, j;

	char		fontchar;

	fontchar = [controlPoint getChar];
	for (i = 0; i < numberOfPoints; i++)
		OpsBuffer[i] = fontchar;
	OpsBuffer[i] = 0;

	[controlPoint selectFont:FONTSIZE];
	
	[[displayTimesMatrix cellAt:cell :0] setStringValue:""];
	    PSgsave();
		PSWEraseView();
		PSWMarkTime (); NXPing();
			for (i = indexOfPoints+2, j = 0;
			     i < indexOfPoints + (numberOfPoints*2);
			     i++, j++)
				XYBuffer[j] = XYPoints[i] - XYPoints[i-2];
				
			XYBuffer[j++] = 0;
			XYBuffer[j++] = 0;

			if (PSTrace)
				DPSTraceContext(DPSGetCurrentContext(), YES);

			PSWXYShow(XYPoints[indexOfPoints], XYPoints[indexOfPoints+1],
				OpsBuffer, XYBuffer, j);		

			if (PSTrace)
				DPSTraceContext(DPSGetCurrentContext(), NO);
		
		PSWReturnTime (&ElapsedTime);
	    PSgrestore();
	[[displayTimesMatrix cellAt:cell :0] setIntValue:ElapsedTime];

	return self;
}

/* 
 * Messaged by the "display" method.  This method should not be called 
 * directly. 
 */
-drawSelf:(NXRect *)r :(int) count
{
		
	PSWEraseView();

	PSsetgray(NX_BLACK);
	if (drawFlags.flags.basic)
		[self drawBasic:0];
	if (drawFlags.flags.usercache)
		[self drawUserCache:1];
	if (drawFlags.flags.userpath)
		[self drawUserPath:2];
	if (drawFlags.flags.rectop  && [[drawMethodsMatrix cellAt:3 :0] isEnabled])
		[self drawRectOp:3];
	if (drawFlags.flags.composite)
		[self drawComposite:4];
	if (drawFlags.flags.show)
		[self drawShow:5];
	if (drawFlags.flags.xyshow)
		[self drawXYShow:6];

	return self;
}

@end
