/*
 * XDPSuserpath.c
 *
 * (c) Copyright 1990-1994 Adobe Systems Incorporated.
 * All rights reserved.
 * 
 * Permission to use, copy, modify, distribute, and sublicense this software
 * and its documentation for any purpose and without fee is hereby granted,
 * provided that the above copyright notices appear in all copies and that
 * both those copyright notices and this permission notice appear in
 * supporting documentation and that the name of Adobe Systems Incorporated
 * not be used in advertising or publicity pertaining to distribution of the
 * software without specific, written prior permission.  No trademark license
 * to use the Adobe trademarks is hereby granted.  If the Adobe trademark
 * "Display PostScript"(tm) is used to describe this software, its
 * functionality or for any other purpose, such use shall be limited to a
 * statement that this software works in conjunction with the Display
 * PostScript system.  Proper trademark attribution to reflect Adobe's
 * ownership of the trademark shall be given whenever any such reference to
 * the Display PostScript system is made.
 * 
 * ADOBE MAKES NO REPRESENTATIONS ABOUT THE SUITABILITY OF THE SOFTWARE FOR
 * ANY PURPOSE.  IT IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY.
 * ADOBE DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
 * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NON- INFRINGEMENT OF THIRD PARTY RIGHTS.  IN NO EVENT SHALL ADOBE BE LIABLE
 * TO YOU OR ANY OTHER PARTY FOR ANY SPECIAL, INDIRECT, OR CONSEQUENTIAL
 * DAMAGES OR ANY DAMAGES WHATSOEVER WHETHER IN AN ACTION OF CONTRACT,
 * NEGLIGENCE, STRICT LIABILITY OR ANY OTHER ACTION ARISING OUT OF OR IN
 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.  ADOBE WILL NOT
 * PROVIDE ANY TRAINING OR OTHER SUPPORT FOR THE SOFTWARE.
 * 
 * Adobe, PostScript, and Display PostScript are trademarks of Adobe Systems
 * Incorporated which may be registered in certain jurisdictions
 * 
 * Author:  Adobe Systems Incorporated
 */

#include <DPS/dpsXclient.h>
#include <DPS/dpsops.h>
#include "DPS/dpsXuserpath.h"

typedef struct _t_NumStrHeader {
    unsigned char type;
    unsigned char representation;
    unsigned short length;
} NumStrHeader;

void PSDoUserPath(coords, numCoords, numType, ops, numOps, bbox, action)
    DPSPointer coords;
    int numCoords;
    DPSNumberFormat numType;
    DPSUserPathOp *ops;
    int numOps;
    DPSPointer bbox;
    DPSUserPathAction action;
{
    DPSDoUserPath(DPSGetCurrentContext(), coords, numCoords, numType, ops,
		  numOps, bbox, action);
}

void DPSDoUserPath(ctxt, coords, numCoords, numType, ops, numOps, bbox, action)
    DPSContext ctxt;
    DPSPointer coords;
    int numCoords;
    DPSNumberFormat numType;
    DPSUserPathOp *ops;
    int numOps;
    DPSPointer bbox;
    DPSUserPathAction action;
{
    typedef struct {
	unsigned char tokenType;
	unsigned char topLevelCount;
	unsigned short nBytes;
	DPSBinObjGeneric obj0;
	DPSBinObjGeneric obj1;
	DPSBinObjGeneric obj2;
	DPSBinObjGeneric obj3;
    } _dpsQ;
    static _dpsQ _dpsF = {
	DPS_DEF_TOKENTYPE, 0, 36, /* will fill in topLevelCount later */
	{DPS_LITERAL|DPS_ARRAY, 0, 2, 16},
	{DPS_EXEC|DPS_NAME, 0, DPSSYSNAME, 0},    
	{DPS_LITERAL|DPS_STRING, 0, 0, 32}, /* param nums */
	{DPS_LITERAL|DPS_STRING, 0, 0, 32}, /* param ops */
    }; /* _dpsQ */
    register DPSBinObjRec *_dpsP = (DPSBinObjRec *)&_dpsF.obj0;
    register int _dps_offset = 32;
    int needBBox, hasUCache, numberSize;
    DPSUserPathOp setbboxOp;
    NumStrHeader nsHeader;

    if (numType >= dps_short && numType < dps_float) numberSize = 2;
    else numberSize = 4;

    hasUCache = (*ops == dps_ucache);

    if (hasUCache) {
	needBBox = (numOps > 1 && ops[1] != dps_setbbox);
    } else needBBox = (*ops != dps_setbbox);

    if (needBBox) {
	numOps += 1;
	setbboxOp = dps_setbbox;
    }
    
    numCoords += 4; /* Account for bbox */

    nsHeader.type = 149; /* Homogeneous Number Array */
    nsHeader.representation = numType;
    nsHeader.length = numCoords;
  
    /* If we're using the send operation, we modify the sequence so that
       it never gets to the action.  This leaves a hole in the sequence,
       but that's ok. */

    if (action == dps_send) _dpsF.topLevelCount = 1;
    else _dpsF.topLevelCount = 2;

    _dpsP[1].val.nameVal = action;
    _dpsP[2].length = (sizeof(NumStrHeader) + numCoords * numberSize);
    _dpsP[3].length = numOps;
    _dpsP[3].val.stringVal = _dps_offset;
    _dps_offset += numOps;
    _dpsP[2].val.stringVal = _dps_offset;
    _dps_offset += _dpsP[2].length;
    _dpsF.nBytes = _dps_offset+4;

    if (needBBox) numOps -= 1;

    numCoords -= 4; /* Unaccount for bbox */

    DPSBinObjSeqWrite(ctxt, (char *) &_dpsF, 36);
    if (needBBox) {
	if (hasUCache) {
	    DPSWriteStringChars(ctxt, (char *) ops, 1);
	    ops++; numOps--;
	}
	DPSWriteStringChars(ctxt, (char *) &setbboxOp, 1);
    }
    DPSWriteStringChars(ctxt, (char *) ops, numOps);
    DPSWriteStringChars(ctxt, (char *) &nsHeader, sizeof(NumStrHeader));
    DPSWriteStringChars(ctxt, (char *) bbox, 4 * numberSize);
    DPSWriteStringChars(ctxt, (char *) coords, numCoords * numberSize);
}

Bool PSHitUserPath(x, y, radius,
		   coords, numCoords, numType, ops, numOps, bbox, action)
    double x, y, radius;
    DPSPointer coords;
    int numCoords;
    DPSNumberFormat numType;
    DPSUserPathOp *ops;
    int numOps;
    DPSPointer bbox;
    DPSUserPathAction action;
{
    return DPSHitUserPath(DPSGetCurrentContext(), x, y, radius,
			  coords, numCoords, numType, ops,
			  numOps, bbox, action);
}

Bool DPSHitUserPath(ctxt, x, y, radius,
		       coords, numCoords, numType, ops, numOps, bbox, action)
    DPSContext ctxt;
    double x, y, radius;
    DPSPointer coords;
    int numCoords;
    DPSNumberFormat numType;
    DPSUserPathOp *ops;
    int numOps;
    DPSPointer bbox;
    DPSUserPathAction action;
{
    float aCoords[5];
    DPSUserPathOp aOps[1];
    float aBbox[4];
    int result;

    if (radius != 0.0) {
	aCoords[0] = x;
	aCoords[1] = y;
	aCoords[2] = radius;
	aCoords[3] = 0.0;
	aCoords[4] = 360.0;
	aOps[0] = dps_arc;
	aBbox[0] = x - radius;
	aBbox[1] = y - radius;
	aBbox[2] = x + radius;
	aBbox[3] = y + radius;

	switch (action) {
	    case dps_infill:
	    case dps_ineofill:
	    case dps_instroke:
		DPSDoUserPath(ctxt, (DPSPointer) aCoords, 5, dps_float,
			      aOps, 1, (DPSPointer) aBbox, action);
		break;
	    case dps_inufill:
	    case dps_inueofill:
	    case dps_inustroke:
		DPSDoUserPath(ctxt, (DPSPointer) aCoords, 5, dps_float,
			      aOps, 1, (DPSPointer) aBbox, dps_send);
		DPSDoUserPath(ctxt, coords, numCoords, numType, ops,
			      numOps, bbox, action);
		break;
	    default:
		return False;
	}
	DPSgetboolean(ctxt, &result);

    } else {
	switch (action) {
	    case dps_infill:
	        DPSinfill(ctxt, x, y, &result);
		break;
	    case dps_ineofill:
		DPSineofill(ctxt, x, y, &result);
		break;
	    case dps_instroke:
		DPSinstroke(ctxt, x, y, &result);
		break;
	    case dps_inufill:
	    case dps_inueofill:
	    case dps_inustroke:
		DPSsendfloat(ctxt, x);
		DPSsendfloat(ctxt, y);
		DPSDoUserPath(ctxt, coords, numCoords, numType, ops,
			      numOps, bbox, action);
		DPSgetboolean(ctxt, &result);
		break;
	    default:
		return False;
	    }
    }
    return result;
}

