/* 
 * UserPath.m by Bruce Blumberg, NeXT Computer, Inc.
 *
 * You may freely copy,distribute and re-use the code in this example. NeXT
 * disclaims any warranty of any kind, expressed or implied, as to its fitness
 * for any particular purpose
 *
 */

#import "UserPath.h"
#import <mach/mach_init.h>
#import <appkit/graphics.h>
#import <appkit/errors.h>
#import <math.h>
#import <libc.h>

static NXZone      *upZone = NULL;

NXZone *userPathZone()
/* Creates a unique zone for use by all user paths */
{
    if (!upZone) {
	upZone = NXCreateZone(vm_page_size, vm_page_size, 1);
    }
    
    return upZone;
}

UserPath *newUserPath()
/* Creates a new User Path in the zone returned by userPathZone */
{
    UserPath    *up;

    up = (UserPath *)NXZoneMalloc(userPathZone(), sizeof(UserPath));
    up->max = 8192;	// JDC
    up->points = (float *)NXZoneMalloc(userPathZone(),
    				       sizeof(float) * up->max);
    up->ops = (char *)NXZoneMalloc(userPathZone(),
    				   (2 + (up->max / 2)) * sizeof(char));
    up->ping = NO;
    
    return up;
}

void freeUserPath(UserPath *up)
/* Frees User Path and its associated buffers */
{
    free(up->points);
    free(up->ops);
    free(up);
    
    return;
}

void growUserPath(UserPath *up)
/*
 * grows the  associated buffers as necessary. buffer size doubles on each
 * call. You never need to call grow directly as it is called as needed by the
 * methods and functions which add elements into the buffer
 */
{
 /* double the size of the internal buffers */
printf ("growUserPath\n");
    up->max *= 2;
    up->points = (float *)NXZoneRealloc(userPathZone(), up->points,
					sizeof(float) * up->max);
    up->ops = (char *)NXZoneRealloc(userPathZone(), up->ops,
    				    (2 + (up->max / 2)) * sizeof(char));

    return;
}

void beginUserPath(UserPath *up, BOOL cache)
/*
 * Call this to start generating a user path. The cache argument specifies if
 * you want the user path cached at the server (i.e. dps_ucache). In either
 * case, the UserPath object will automatically calculate the bounding box for
 * the path and add the dps_setbbox operator.
 */
{
    up->numberOfPoints = up->numberOfOps = 0;
    up->cp.x = up->cp.y = 0;
    up->bbox[0] = up->bbox[1] = 1.0e6;
    up->bbox[2] = up->bbox[3] = -1.0e6;
    if (cache) {
	up->ops[up->numberOfOps++] = dps_ucache;
    }
    up->ops[up->numberOfOps++] = dps_setbbox;
    up->opForUserPath = 0;
    
    return;
}

void endUserPath(UserPath *up, int op)
/*
 * Call this to stop filling the path. Note this does not send the userpath to
 * the server -- use sendUserPath. The op argument should be one of the
 * following:
 *	dps_uappend, dps_ufill ,dps_ueofill, dps_ustroke, dps_ustrokepath,
 *	dps_inufill, dps_inueofill, dps_inustroke, dps_def, dps_put.
 * These are defined in <dpsclient/dpsNext.h.  
 */
{
    up->opForUserPath = op;
    
    return;
}


void UPdebug(UserPath *up, BOOL shouldPing)
/*
 * Sets ping to YES so that after each time a user path is sent down to the
 * window server, an NXPing() is sent after. The purpose is to catch PostScript
 * errors that may be generated by the user path. sendUserPath brackets the
 * download and the NXPing() in an NX_DURING... NX_HANDLER construct. Normally
 * ping is NO. 
 */
{
    up->ping = shouldPing;
    
    return;
}

int sendUserPath(UserPath *up)
/*
 * Call this to send the path down to the server. If ping==YES (set via
 * debug:), the function will send an NXPing() after the Path. In any event,
 * code is bracketed by a NX_DURING ... NX_HANDLER construct which will try to
 * catch postscript errors.  If ping==NO (the default) it is unlikely to catch
 * errors, with ping==YES it will. Whether you can recover or not is another
 * matter. sendUserPath returns 0 on success and -1 on failure. If no previous
 * endUserPath: has been sent, will return -2 and will not send the path to the
 * server.
 */
{
    NXHandler           exception;

    exception.code = 0;
    if (up->opForUserPath != 0) {
      NX_DURING
	DPSDoUserPath(up->points, up->numberOfPoints, dps_float, up->ops,
		      up->numberOfOps, up->bbox, up->opForUserPath);
	if (up->ping) {
	    NXPing();
	}
	
      NX_HANDLER
	exception = NXLocalHandler;
      NX_ENDHANDLER
	if (exception.code) {
	    NXReportError(&exception);
	    if (exception.code == dps_err_ps) {
		return -1;
	    }
	} else {
	    return 0;
	}
    }
    
    return -1;
}


void UPmoveto(UserPath *up, float x, float y)
/* adds <x y moveto> to user path and updates bounding box */
{
    up->ops[up->numberOfOps++] = dps_moveto;
    up->points[up->numberOfPoints++] = x;
    up->points[up->numberOfPoints++] = y;
 
    if (x < up->bbox[0]) {
	up->bbox[0] = x;
    }
    if (y < up->bbox[1]) {
	up->bbox[1] = y;
    }
    if (x > up->bbox[2]) {
	up->bbox[2] = x;
    }
    if (y > up->bbox[3]) {
	up->bbox[3] = y;
    }
   
    return;
}


void UPlineto(UserPath *up, float x, float y)
/* adds <x y lineto> to user path and updates bounding box */
{
    up->ops[up->numberOfOps++] = dps_lineto;
    up->points[up->numberOfPoints++] = x;
    up->points[up->numberOfPoints++] = y;
 
    if (x < up->bbox[0]) {
	up->bbox[0] = x;
    }
    if (y < up->bbox[1]) {
	up->bbox[1] = y;
    }
    if (x > up->bbox[2]) {
	up->bbox[2] = x;
    }
    if (y > up->bbox[3]) {
	up->bbox[3] = y;
    }
    return;
}

