/*
	Copyright (c) 2024 PTC Inc. and/or Its Subsidiary Companies. All Rights Reserved.
*/



/*--------------------------------------------------------------------*\
Pro/TOOLKIT includes
\*--------------------------------------------------------------------*/
#include <ProToolkit.h>
#include <ProArray.h>
#include <ProExtobj.h>
#include <ProExtobjCB.h>
#include <ProExtobjDisp.h>
#include <ProExtobjRef.h>
#include <ProExtobjSel.h>
#include <ProMenu.h>
#include <ProMdl.h>
#include <ProModelitem.h>
#include <ProObjects.h>
#include <ProSelection.h>
#include <ProSurface.h>
#include <ProUtil.h>
#include <ProVerstamp.h>
/*--------------------------------------------------------------------*\
C System includes
\*--------------------------------------------------------------------*/

/*--------------------------------------------------------------------*\
Application includes
\*--------------------------------------------------------------------*/
#include "TestError.h"
#include "TestFiletypes.h"
#include "UtilFiles.h"
#include "UtilMatrix.h"
#include "UtilMessage.h"
#include "UtilString.h"
#include "UtilCollect.h"
#include "UtilVisit.h"
#include "UtilMath.h"
#include "PTApplsUnicodeUtils.h"
/*--------------------------------------------------------------------*\
Application macros
\*--------------------------------------------------------------------*/
#ifndef FALSE
#define FALSE 0
#endif

#ifndef TRUE
#define TRUE  1
#endif

#define ARROW_NUM_SEGMENTS 9
#define HW	0.05
#define HL	0.3

/*--------------------------------------------------------------------*\
Application data types
\*--------------------------------------------------------------------*/
typedef struct
{
    int   ref_id;
    char *stamp_str;
} RefVersion;

typedef struct
{
    ProMdl model;
    FILE *fp;
    ProLinedata *lines;
    ProMatrix *transf;
} AppData;

/*--------------------------------------------------------------------*\
Application global/external data
\*--------------------------------------------------------------------*/
static ProExtobjClass mesh_class, arrow_class, extobjclass;

static ProMatrix identity_matrix = { {1.0, 0.0, 0.0, 0.0},
                                     {0.0, 1.0, 0.0, 0.0},
                                     {0.0, 0.0, 1.0, 0.0},
                                     {0.0, 0.0, 0.0, 1.0}};
static RefVersion *refVersionTable = NULL;
static int tableSize = 0;
static AppData app_data;
static ProLinedata line;
static int is_mesh_class = 0, is_arrow_class = 0;

typedef int (*ProUtilMeshAct)( 
    ProSurface *surface,
    double uv[2],	/* I - The UV values */
    int start,		/* I - 1 if this is the start of a new mesh line */
    ProAppData tmp_app_data);
    
ProError ProTestDeleteExtobj(
    ProExtobj *p_extobj,    /*In  : external object */
    ProError  status,	    /*In  : status */ 
    ProAppData app_data);    /*In  : app data */
    
/*====================================================================*\
    FUNCTION :	ProTestMeshPointsAct()
    PURPOSE  :	Test function (to be called at each surface mesh point)
		to draw and dump mesh coordinates.
\*====================================================================*/
int ProTestMeshPointsAct(
    ProSurface *surface,/* I - The surface */
    double uv[2],	/* I - The UV values */
    int start,		/* I - 1 if this is the start of a new mesh line */
    ProAppData tmp_app_data)/* I - General data */
			/* Return 0        - continue,
				  non-zero - terminate meshing */
{
    ProError status;
    ProVector xyz, der1[2], der2[3], normal;
    AppData *data = (AppData *) tmp_app_data;

    if (data->lines == NULL)
    {
        status = ProArrayAlloc (0, sizeof (ProLinedata), 1,
            (ProArray*)&(data->lines));
    }
/*--------------------------------------------------------------------*\
    Get the xyz location for this surface point.
\*--------------------------------------------------------------------*/
    status = ProSurfaceXyzdataEval(*surface, uv, xyz, der1, der2, normal);
    TEST_CALL_REPORT("ProSurfaceXyzdataEval()", "ProTestMeshAct()",
					status, status != PRO_TK_NO_ERROR);

/*--------------------------------------------------------------------*\
    Draw (or start) a line
\*--------------------------------------------------------------------*/
    if(start)
    {
        line.end1[0] = xyz[0];
        line.end1[1] = xyz[1];
        line.end1[2] = xyz[2];
        line.type = PRO_ENT_LINE;
    }
    else
    {
        line.end2[0] = xyz[0];
        line.end2[1] = xyz[1];
        line.end2[2] = xyz[2];
	status = ProArrayObjectAdd((ProArray*)&(data->lines), PRO_VALUE_UNUSED,
            1, &line);
        line.end1[0] = xyz[0];
        line.end1[1] = xyz[1];
        line.end1[2] = xyz[2];
        line.type = PRO_ENT_LINE;
    }

    return(0);
}

/*====================================================================*\
    FUNCTION :	ProTestArrowPointsAct()
    PURPOSE  :	Test function (to be called at each surface arrow point)
		to draw and dump arrow coordinates.
\*====================================================================*/
int ProTestArrowPointsAct(
    ProSurface *surface,/* I - The surface */
    double uv[2],	/* I - The UV values */
    int start,		/* I - 1 if this is the start of a new mesh line */
    ProAppData tmp_app_data)/* I - General data */
			/* Return 0        - continue,
				  non-zero - terminate meshing */
{
    ProError status;
    ProVector xyz, der1[2], der2[3], normal;
    AppData *data = (AppData *) tmp_app_data;
    ProMatrix transf;

    if (data->transf == NULL)
    {
        status = ProArrayAlloc (0, sizeof (ProMatrix), 1,
            (ProArray*)&(data->transf));
    }
/*--------------------------------------------------------------------*\
    Get the xyz location for this surface point.
\*--------------------------------------------------------------------*/
    status = ProSurfaceXyzdataEval(*surface, uv, xyz, der1, der2, normal);
    TEST_CALL_REPORT("ProSurfaceXyzdataEval()", "ProTestMeshAct()",
					status, status != PRO_TK_NO_ERROR);
    ProUtilVectorNormalize (normal, normal);
    ProUtilVectorNormalize (der1[0], der1[0]);
    ProUtilVectorNormalize (der1[1], der1[1]);
    status = ProUtilVectorsToTransf (normal, der1[0], der1[1], xyz, transf);
    status = ProArrayObjectAdd((ProArray*)&(data->transf), PRO_VALUE_UNUSED,
            1, &transf);
    return(0);
}

/*====================================================================*\
    FUNCTION :  ProUtilCollectMeshPoints()
    PURPOSE  :  Make a UV mesh over a specified surface
\*====================================================================*/
int ProUtilCollectMeshPoints(
    ProSurface *surface,
    double resolution,          /* The step size in model unit */
    int nlines[2],              /* No of U and V lines */
    ProUtilMeshAct action,      /* Function to call at each mesh point */
    ProAppData tmp_app_data)    /* General data */
{
    ProError status;
    ProGeomitemdata  *sdata;
    double u_min, v_min, u_max, v_max, u_step, v_step, u_res, v_res,
                                uv[2], last_uv[2], der1[2][3];
    ProUvStatus uvstatus;
    int start = 0, error;
    ProSolid solid;

    solid = (ProSolid) ((AppData*)tmp_app_data)->model;
/*--------------------------------------------------------------------*\
    Get the maxmum and minium U and V for the surface
\*--------------------------------------------------------------------*/
    status = ProSurfaceDataGet(*surface, &sdata);
    TEST_CALL_REPORT("ProSurfaceDataGet()","ProUtilSurfaceMesh()",
                        status, status != PRO_TK_NO_ERROR);

/*--------------------------------------------------------------------*\
    Calculate the U and V parameters
\*--------------------------------------------------------------------*/
    u_min = sdata->data.p_surface_data->uv_min[0];
    v_min = sdata->data.p_surface_data->uv_min[1];
    u_max = sdata->data.p_surface_data->uv_max[0];
    v_max = sdata->data.p_surface_data->uv_max[1];

    ProGeomitemdataFree(&sdata);
    TEST_CALL_REPORT("ProGeomitemdataFree()","ProUtilSurfaceMesh()",
                        status, status != PRO_TK_NO_ERROR);

    u_step = (u_max - u_min) / (nlines[0] + 1);
    v_step = (v_max - v_min) / (nlines[1] + 1);

/*--------------------------------------------------------------------*\
    Calculate the U and V resolution to give the correct resolution in
    model units.
\*--------------------------------------------------------------------*/
    uv[0] = (u_max + u_min) / 2.0;
    uv[1] = (v_max + v_min) / 2.0;
    status = ProSurfaceXyzdataEval(*surface, uv, NULL, der1, NULL, NULL);
    TEST_CALL_REPORT("ProSurfaceXyzdataEval()","ProUtilSurfaceMesh()",
                        status, status != PRO_TK_NO_ERROR);
    if (resolution == 0.0)
    {
        u_res = u_step;
        v_res = v_step;
    }
    else
    {
        u_res = resolution / ProUtilVectorLength(der1[0]);
        v_res = resolution / ProUtilVectorLength(der1[1]);
    }
/*--------------------------------------------------------------------*\
        Adjust the upper limits to ensure that we get a mesh line at the max
\*--------------------------------------------------------------------*/
        u_max += u_res/ 2.00;
        v_max += v_res/ 2.00;
    

/*--------------------------------------------------------------------*\
    Do lines of constant U
\*--------------------------------------------------------------------*/
    
    for(uv[0] =  u_min;
        uv[0] <= u_max;
        uv[0] += u_step)
    {
        last_uv[1] = -1000000.0;
        for(uv[1] =  v_min;
            uv[1] <= v_max;
            uv[1] += v_res)
        {
/*--------------------------------------------------------------------*\
            If this point is outside the domain, skip it
\*--------------------------------------------------------------------*/
            status = ProSurfaceUvpntVerify(solid, *surface, uv, &uvstatus);
            TEST_CALL_REPORT("ProSurfaceUvpntVerify()", "ProUtilSurfaceMesh()",
                                        status, status != PRO_TK_NO_ERROR);
            if(uvstatus == PRO_UV_OUTSIDE)
            {
                continue;
            }

            start = (uv[1] - last_uv[1]) > (u_res + EPSM6);

            error = (*action)(surface, uv, start, tmp_app_data);
            if(error != 0)
                return(error);

            last_uv[1] = uv[1];
        }
    }
/*--------------------------------------------------------------------*\
    Return if collect arrow points
\*--------------------------------------------------------------------*/ 
if (u_step == u_res && v_step == v_res)
    return (0);
/*--------------------------------------------------------------------*\
    Do lines of constant V
\*--------------------------------------------------------------------*/
    for(uv[1] =  v_min;
        uv[1] <= v_max;
        uv[1] += v_step)
    {
        last_uv[0] = -1000000.0;
        for(uv[0] =  u_min;
            uv[0] <= u_max;
            uv[0] += u_res)
        {
/*--------------------------------------------------------------------*\
            If this point is outside the domain, skip it
\*--------------------------------------------------------------------*/
            status = ProSurfaceUvpntVerify(solid, *surface, uv, &uvstatus);
            TEST_CALL_REPORT("ProSurfaceUvpntVerify()", "ProUtilSurfaceMesh()",
                                        status, status != PRO_TK_NO_ERROR);
            if(uvstatus == PRO_UV_OUTSIDE)
            {
                continue;
            }

            start = (uv[0] - last_uv[0]) > (v_res + EPSM6);
            
            error = (*action)(surface, uv, start, tmp_app_data);
            if(error != 0)
                return(error);

            last_uv[0] = uv[0];
        }
    }

    return(0);
}

/*====================================================================*\
  Function : FindRefIndex
  Purpose  : Searches the refVersionTable for an external object reference
             If an entry does not exist, space is allocated for it
\*====================================================================*/
int FindRefIndex( int ref_id)
{
    int i;

    if( refVersionTable == NULL )
    {
	refVersionTable = (RefVersion *)calloc(1, sizeof(RefVersion));
	refVersionTable[0].ref_id = ref_id;
	refVersionTable[0].stamp_str = NULL;

	tableSize = 1;
	i = 0;
    }
    else
    {
	i = 0;

	while( i < tableSize && refVersionTable[i].ref_id != ref_id )
	    i++;
        
	if( i >= tableSize )
	{
	    refVersionTable = (RefVersion *)realloc((void *)refVersionTable,
						    (sizeof(RefVersion) * (i+1)));
	    refVersionTable[i].ref_id = ref_id;
	    refVersionTable[i].stamp_str = NULL;

	    tableSize++;
	}
    }

    return(i);
}

/*====================================================================*\
  Function : get_vectors
  Purpose  : Get components of local system
\*====================================================================*/
void get_vectors(
    double loc_sys[4][3],   /* Matrix to extract data from.
                                 == NULL iff unit
                                  transformation is needed. */
    double  x_vec[3],       /* == NULL iff no output needed */
    double  y_vec[3],       /* == NULL iff no output needed */
    double  z_vec[3],       /* == NULL iff no output needed */
    double  o_pnt[3])       /* == NULL iff no output needed */

{

    if( loc_sys != NULL )
    {
        if( x_vec != NULL )
        {
            x_vec[0] = loc_sys[0][0];
            x_vec[1] = loc_sys[1][0];
            x_vec[2] = loc_sys[2][0];
        }
        if( y_vec != NULL )
        {
            y_vec[0] = loc_sys[0][1];
            y_vec[1] = loc_sys[1][1];
            y_vec[2] = loc_sys[2][1];
        }
        if( z_vec != NULL )
        {
            z_vec[0] = loc_sys[0][2];
            z_vec[1] = loc_sys[1][2];
            z_vec[2] = loc_sys[2][2];
        }
        if( o_pnt != NULL )
        {
            o_pnt[0] = loc_sys[3][0];
            o_pnt[1] = loc_sys[3][1];
            o_pnt[2] = loc_sys[3][2];
        }
    }
    else
    {
        if( x_vec != NULL )
        {
            x_vec[0] = 1.0;
            x_vec[1] = 0.0;
            x_vec[2] = 0.0;
        }
        if( y_vec != NULL )
        {
            y_vec[0] = 0.0;
            y_vec[1] = 1.0;
            y_vec[2] = 0.0;
        }
        if( z_vec != NULL )
        {
            z_vec[0] = 0.0;
            z_vec[1] = 0.0;
            z_vec[2] = 1.0;
        }
        if( o_pnt != NULL )
        {
            o_pnt[0] = 0.0;
            o_pnt[1] = 0.0;
            o_pnt[2] = 0.0;
        }
    }
}


/*====================================================================*\
  Function : put_vectors
  Purpose  : Put components of a local system
  Note     : NULL may be passed instead of any of:
             x_vec , y_vec, z_vec, o_pnt.
             In this case corresponding elements in
             loc_sys WILL NOT BE SET
\*====================================================================*/
void put_vectors(
    double  loc_sys[4][3],	/* Matrix to put data into.       */
    double  x_vec[3],
    double  y_vec[3],
    double  z_vec[3],
    double  o_pnt[3])

{

    if( x_vec != NULL )
    {
        loc_sys[0][0] = x_vec[0];
        loc_sys[1][0] = x_vec[1];
        loc_sys[2][0] = x_vec[2];
    }
    if( y_vec != NULL )
    {
        loc_sys[0][1] = y_vec[0];
        loc_sys[1][1] = y_vec[1];
        loc_sys[2][1] = y_vec[2];
    }
    if( z_vec != NULL )
    {
        loc_sys[0][2] = z_vec[0];
        loc_sys[1][2] = z_vec[1];
        loc_sys[2][2] = z_vec[2];
    }
    if( o_pnt != NULL )
    {
        loc_sys[3][0] = o_pnt[0];
        loc_sys[3][1] = o_pnt[1];
        loc_sys[3][2] = o_pnt[2];
    }
}

/*====================================================================*\
  Function : matrix_to_matrix4
  Purpose  : Convert 4x3 matrix to a 4x4 matrix
\*====================================================================*/
void matrix_to_matrix4(double input[4][3], ProMatrix output)
{
   int i,j;

    if( input != NULL )
    {
        for (i = 0; i < 3; i++)
        {
            for (j = 0; j < 3; j++)
                output[i][j] = input[j][i];
            output[i][3] = 0.0;
        }

        for (j = 0; j < 3; j++)
            output[3][j] = input[3][j];

        output[3][3] = 1.0;
    }
    else
    {
	ProUtilMatrixCopy(identity_matrix, output);
    }
}

/*====================================================================*\
  Function : ProTestExtobjCB
  Purpose  : Callback function for external object
\*====================================================================*/
ProError ProTestExtobjCB(ProExtobj **extobject, ProExtobjClass *extobjclass,
			 ProAppData *ntf_data, int n_objects)
{
    return( PRO_TK_NO_ERROR );
}

/*====================================================================*\
  Function : ProTestExtobjRefCB
  Purpose  : Callback function for external object references
\*====================================================================*/
ProError ProTestExtobjRefCB(ProExtobj **extobject, ProExtobjClass *extobjclass,
			    ProAppData *ntf_data, int n_objects)
{
    ProError status;
    ProMdl model;
    char fname[PRO_NAME_SIZE];

    if( app_data.fp == NULL )
    {
        status = ProMdlCurrentGet(&model);
        TEST_CALL_REPORT("ProMdlCurrentGet()", "ProTestExtobj()", status,
						status != PRO_TK_NO_ERROR);
        ProTestQcrName(&model, (char *)EXTERNAL_OBJ, fname);
        app_data.fp = PTApplsUnicodeFopen(fname, "a");

        ProTKFprintf(app_data.fp, "External object reference has been modified, deleted, or "
                    "suppressed...\n");
	fclose(app_data.fp);
	app_data.fp = NULL;
    }
    else
    {
        ProTKFprintf(app_data.fp, "External object reference has been modified, deleted, or "
                    "suppressed...\n");
    }

    return( PRO_TK_NO_ERROR );
}

/*====================================================================*\
  Function : ProTestExtobjRefCBDelete
  Purpose  : Callback function for external object references
\*====================================================================*/
ProError ProTestExtobjRefCBDelete(ProExtobj **extobject, 
    ProExtobjClass *extobjclass,
    ProAppData *ntf_data, int n_objects)
{
    ProError status;
    int i;
    
    TEST_CALL_REPORT ("ProExtobjCBAct()", "ProTestExtobjRefCBDelete()",
        PRO_TK_NO_ERROR, 0);
    for (i = 0; i < n_objects; i++)
    {
        status = ProTestDeleteExtobj (extobject[i], PRO_TK_NO_ERROR,
            (ProAppData)NULL);
    }
    
    return (PRO_TK_NO_ERROR);
}

/*====================================================================*\
  Function : ProTestExtobjRefCBModify
  Purpose  : Callback function for external object references
\*====================================================================*/
ProError ProTestExtobjRefCBModify(ProExtobj **extobject, 
    ProExtobjClass *extobjclass,
    ProAppData *ntf_data, int n_objects)
{
    TEST_CALL_REPORT ("ProExtobjCBAct()", "ProTestExtobjRefCBModify()",
        PRO_TK_NO_ERROR, 0);
        
    return (PRO_TK_NO_ERROR);
}

/*====================================================================*\
  Function : ProTestExtobjOwnCBModify
  Purpose  : Callback function for external object references
\*====================================================================*/
ProError ProTestExtobjOwnCBModify(ProExtobj **extobject, 
    ProExtobjClass *extobjclass,
    ProAppData *ntf_data, int n_objects)
{

    TEST_CALL_REPORT ("ProExtobjCBAct()", "ProTestExtobjOwnCBModify()",
        PRO_TK_NO_ERROR, 0);
    return (PRO_TK_NO_ERROR);
}

/*====================================================================*\
  Function : ProTestExtobjOwnCBSuppress
  Purpose  : Callback function for external object references
\*====================================================================*/
ProError ProTestExtobjOwnCBSuppress(ProExtobj **extobject, 
    ProExtobjClass *extobjclass,
    ProAppData *ntf_data, int n_objects)
{

    TEST_CALL_REPORT ("ProExtobjCBAct()", "ProTestExtobjOwnCBSuppress()",
        PRO_TK_NO_ERROR, 0);
    return (PRO_TK_NO_ERROR);
}

/*====================================================================*\
  Function : ProTestExtobjOwnCBDelete
  Purpose  : Callback function for external object references
\*====================================================================*/
ProError ProTestExtobjOwnCBDelete(ProExtobj **extobject, 
    ProExtobjClass *extobjclass,
    ProAppData *ntf_data, int n_objects)
{

    TEST_CALL_REPORT ("ProExtobjCBAct()", "ProTestExtobjOwnCBDelete()",
        PRO_TK_NO_ERROR, 0);
    return (PRO_TK_NO_ERROR);
}

/*====================================================================*\
  Function : ProTestExtobjRefCBSuppress
  Purpose  : Callback function for external object references
\*====================================================================*/
ProError ProTestExtobjRefCBSuppress(ProExtobj **extobject, 
    ProExtobjClass *extobjclass,
    ProAppData *ntf_data, int n_objects)
{
    ProError status;
    int i ;
    ProWExtobjdata extobj_data;
    ProExtobjDispprops props[1] = {PRO_EXTOBJ_BLANKED};
    
    TEST_CALL_REPORT ("ProExtobjCBAct()", "ProTestExtobjRefCBSuppress()",
        PRO_TK_NO_ERROR, 0);
    for (i = 0; i < n_objects; i++)
    {
        status = ProExtobjdataGet (extobject[i], extobjclass, 
            PRO_EXTOBJDAT_DISPLAY, &extobj_data);
        TEST_CALL_REPORT ("ProExtobjdataGet()", "ProTestExtobjRefCBSuppress()",
            status, status != PRO_TK_NO_ERROR);
        status = ProDispdatPropsSet (extobj_data, props, 1);
        TEST_CALL_REPORT ("ProDispdatPropsSet()", 
            "ProTestExtobjRefCBSuppress()",
            status, status != PRO_TK_NO_ERROR);
        status = ProExtobjdataSet (extobject[i], extobjclass, 
            extobj_data);
        TEST_CALL_REPORT ("ProExtobjdataSet()", "ProTestExtobjRefCBSuppress()",
            status, status != PRO_TK_NO_ERROR);
    }
    return PRO_TK_NO_ERROR;
}

/*====================================================================*\
  Function : ProTestExtobjCreate
  Purpose  : Creates an external object 
\*====================================================================*/
int ProTestExtobjCreate (
    ProSelection surface,
    ProExtobjClass *class_name, 
    ProLinedata *lines,
    ProMatrix   transf)
{
    ProError    status;
    ProMdl      model;
    ProModelitem    owner;
    ProExtobj   result_obj;
    ProWExtobjdata disp_data = NULL;
    int         lines_num, i, j, num_box;
    ProCurvedata *curvedata;
    ProWExtobjdata	sel_data = NULL;
    ProSelbox		sel_box[16], *sel_box_ptr;
    ProWExtobjRef  newRef2;
    static ProExtobjDispprops props[2] = {PRO_EXTOBJ_SPIN_INVARIANT,
				      PRO_EXTOBJ_ZOOM_INVARIANT};
       
    status = ProMdlCurrentGet (&model);
    TEST_CALL_REPORT ("ProMdlCurrentGet()", "ProTestExtobjCreate()",
        status, status != PRO_TK_NO_ERROR);
    status = ProMdlToModelitem(model, &owner);
    TEST_CALL_REPORT("ProModelitemInit()", "ProTestExtobjCreate()", status,
        status != PRO_TK_NO_ERROR);
    status = ProExtobjCreate(class_name, &owner, &result_obj);
    TEST_CALL_REPORT("ProExtobjCreate()", "ProTestExtobjCreate()", status,
        status != PRO_TK_NO_ERROR);
    status = ProExtobjExtidSet (&result_obj, 12345);
    TEST_CALL_REPORT("ProExtobjExtidSet()", "ProTestExtobjCreate()", status,
					status != PRO_TK_NO_ERROR);
    status = ProExtobjExtidGet (&result_obj, &i);
    TEST_CALL_REPORT("ProExtobjExtidGet()", "ProTestExtobjCreate()", status,
					status != PRO_TK_NO_ERROR);
    status = ProExtobjReusableSet (&result_obj);
    TEST_CALL_REPORT("ProExtobjReusableSet()", "ProTestExtobjCreate()", status,
        status != PRO_TK_NO_ERROR);
    status = ProExtobjReusableClear(&result_obj);
    TEST_CALL_REPORT("ProExtobjReusableClear()", "ProTestExtobjCreate()", 
        status,	status != PRO_TK_NO_ERROR);
    status = ProExtobjExttypeSet(&result_obj, 1);
    TEST_CALL_REPORT("ProExtobjExttypeSet()", "ProTestExtobjCreate()", status,
        status != PRO_TK_NO_ERROR);
    status = ProExtobjExttypeGet(&result_obj, &i);
    TEST_CALL_REPORT("ProExtobjExttypeGet()", "ProTestExtobjCreate()", status,
        status != PRO_TK_NO_ERROR);
    status = ProDispdatAlloc (&disp_data);    
    TEST_CALL_REPORT("ProDispdatAlloc()", "ProTestExtobjCreate()", status,
        status != PRO_TK_NO_ERROR);
    status = ProExtobjdataAdd (&result_obj, class_name, disp_data);
    TEST_CALL_REPORT("ProExtobjdataAdd()", "ProTestExtobjCreate()", 
        status, status != PRO_TK_NO_ERROR);
    status = ProDispdatScaleSet (disp_data, 1.0);
    TEST_CALL_REPORT("ProDispdatScaleSet()", "ProTestExtobjCreate()", status,
        status != PRO_TK_NO_ERROR);
    status = ProDispdatColorSet (disp_data, PRO_COLOR_LETTER);
    TEST_CALL_REPORT("ProDispdatColorSet()", "ProTestExtobjCreate()", status,
        status != PRO_TK_NO_ERROR);
    status = ProDispdatLinestyleSet (disp_data, PRO_LINESTYLE_SOLID);
    TEST_CALL_REPORT("ProDispdatLinestyleSet()", "ProTestExtobjCreate()", 
        status, status != PRO_TK_NO_ERROR);
    status = ProDispdatPropsSet(disp_data, &props[0], 0);
    TEST_CALL_REPORT("ProDispdatPropsSet()", "ProTestExtobjCreate()", status,
	status != PRO_TK_NO_ERROR);
    status = ProDispdatTrfSet (disp_data, transf);
    TEST_CALL_REPORT("ProDispdatTrfSet()", "ProTestExtobjCreate()", 
        status, status != PRO_TK_NO_ERROR);
    status = ProArraySizeGet ((ProArray)lines, &lines_num);
    TEST_CALL_REPORT("ProArraySizeGet()", "ProTestExtobjCreate()", 
        status, status != PRO_TK_NO_ERROR);
    curvedata = (ProCurvedata*)calloc (sizeof(ProCurvedata), lines_num);
    for (i = 0; i < lines_num; i++)
    {
        (curvedata+i)->line.type = lines[i].type;
        for (j = 0; j < 3; j++)
        {
            (curvedata+i)->line.end1[j] = lines[i].end1[j];
            (curvedata+i)->line.end2[j] = lines[i].end2[j];
        }
    }
    status = ProDispdatEntsSet (disp_data, curvedata, lines_num);
    TEST_CALL_REPORT("ProDispdatEntsSet()", "ProTestExtobjCreate()", 
        status, status != PRO_TK_NO_ERROR);
    status = ProExtobjdataSet(&result_obj, class_name, disp_data);
    TEST_CALL_REPORT("ProExtobjdataSet()", "ProTestExtobjCreate()", status,
	status != PRO_TK_NO_ERROR);

    free (curvedata);
    status = ProExtobjdataFree (&disp_data);
    TEST_CALL_REPORT ("ProExtobjdataFree", "ProTestExtobjCreate()",
		      status, status != PRO_TK_NO_ERROR);
		     
/*--------------------------------------------------------------------*\
Exercise select data funcs
\*--------------------------------------------------------------------*/

    status = ProSeldatAlloc (&sel_data);
    TEST_CALL_REPORT("ProSeldatAlloc()", "ProTestExtobjCreate()", status,
					status != PRO_TK_NO_ERROR);

    sel_box_ptr = &sel_box[0];
    (*sel_box_ptr)[0][0] = 0.0;
    (*sel_box_ptr)[0][1] = 0.0;
    (*sel_box_ptr)[0][2] = 0.0;
    (*sel_box_ptr)[1][0] = 0.5;
    (*sel_box_ptr)[1][1] = 0.5;
    (*sel_box_ptr)[1][2] = 0.5;
    num_box = 1;
   
    status = ProSeldatSelboxesSet (sel_data, sel_box, num_box);
    TEST_CALL_REPORT("ProSeldatSelboxesSet()", "ProTestExtobjCreate()", status,
					status != PRO_TK_NO_ERROR);
    if ( status != PRO_TK_NO_ERROR ) 
      ProTKPrintf("ProSeldatSelboxesSet failed with status %d\n", status);


    status = ProExtobjdataAdd (&result_obj, class_name, sel_data);
    TEST_CALL_REPORT("ProExtobjdataAdd()", "ProTestExtobjCreate()", status,
					status != PRO_TK_NO_ERROR);
    if ( status != PRO_TK_NO_ERROR ) 
      ProTKPrintf("ProExtobjdataAdd failed to add select data with status %d\n",
		    status);

    status = ProExtobjdataFree (&sel_data);
    TEST_CALL_REPORT ("ProExtobjdataFree", "ProTestExtobjCreate()",
		      status, status != PRO_TK_NO_ERROR);

 
    
    status = ProExtobjRefAlloc(&newRef2);
    TEST_CALL_REPORT("ProExtobjRefAlloc()", "ProTestExtobjCreate()", status,
	status != PRO_TK_NO_ERROR);
    status = ProExtobjReftypeSet(newRef2, 1);
    TEST_CALL_REPORT("ProExtobjReftypeSet()", "ProTestExtobjCreate()", status,
	status != PRO_TK_NO_ERROR);
    status = ProExtobjReftypeGet(newRef2, &i);
    TEST_CALL_REPORT("ProExtobjReftypeGet()", "ProTestExtobjCreate()", status,
	status != PRO_TK_NO_ERROR);
    status = ProExtobjRefselectionSet(newRef2, surface);
    TEST_CALL_REPORT("ProExtobjRefselectionSet()", "ProTestExtobjCreate()",
        status, status != PRO_TK_NO_ERROR);
    status = ProExtobjRefAdd(&result_obj, newRef2);
    TEST_CALL_REPORT("ProExtobjRefAdd()", "ProTestExtobjCreate()", status,
    	status != PRO_TK_NO_ERROR);
    status = ProExtobjRefFree(&newRef2);
    TEST_CALL_REPORT("ProExtobjRefFree()", "ProTestExtobjCreate()", status,
	status != PRO_TK_NO_ERROR);

    if (class_name->type == 0)
    {
        status = ProExtobjWarningEnable (&result_obj, 
            (ProExtobjAction) (PRO_EO_ACT_REF_SUPPR|PRO_EO_ACT_REF_DELETE));
        TEST_CALL_REPORT("ProExtobjWarningEnable()", 
            "ProTestExtobjCreate()", status,
            status != PRO_TK_NO_ERROR);
    }
    else
    {
        status = ProExtobjWarningDisable (&result_obj, 
            (ProExtobjAction) (PRO_EO_ACT_REF_SUPPR|PRO_EO_ACT_REF_DELETE));
        TEST_CALL_REPORT("ProExtobjWarningDisable()", 
            "ProTestExtobjCreate()", status,
            status != PRO_TK_NO_ERROR);
    }
    status = ProExtobjOwnerobjSet(&result_obj, class_name, &owner);
    TEST_CALL_REPORT("ProExtobjOwnerobjSet()", "ProTestExtobjCreate()", status,
	status != PRO_TK_NO_ERROR && status != PRO_TK_BAD_INPUTS);
    return (0);
}

/*====================================================================*\
  Function : ProTestClassCreate
  Purpose  : Creates an external object class 
\*====================================================================*/
int ProTestClassCreate(ProExtobjClass *class_name)
{
    ProError status;
    ProExtobjCallbacks	callbacks;
    
    status = ProExtobjClassCreate(class_name);
    TEST_CALL_REPORT("ProExtobjClassCreate()", "ProTestClassCreate()",
	status, status != PRO_TK_NO_ERROR);
    callbacks.enabled_cbs = 0;
    callbacks.display_CB = (ProExtobjCBAct)ProTestExtobjCB;
    callbacks.select_CB = (ProExtobjCBAct)ProTestExtobjCB;
    callbacks.owner_modify_CB = (ProExtobjCBAct)ProTestExtobjOwnCBModify;
    callbacks.owner_suppress_CB = (ProExtobjCBAct)ProTestExtobjOwnCBSuppress;
    callbacks.owner_delete_CB = (ProExtobjCBAct)ProTestExtobjOwnCBDelete;
    callbacks.ref_modify_CB = (ProExtobjCBAct)ProTestExtobjRefCBModify;
    callbacks.ref_suppress_CB = (ProExtobjCBAct)ProTestExtobjRefCBSuppress;
    callbacks.ref_delete_CB = (ProExtobjCBAct)ProTestExtobjRefCBDelete;
    status = ProExtobjCallbacksSet(class_name, &callbacks);
    TEST_CALL_REPORT("ProExtobjCallbacksSet()", "ProTestClassCreate()",
        status, status != PRO_TK_NO_ERROR);
    status = ProExtobjCBEnable(class_name,
        PRO_EO_ACT_REF_SUPPR|PRO_EO_ACT_REF_DELETE|PRO_EO_ACT_REF_MODIF,
        TRUE);
    TEST_CALL_REPORT("ProExtobjCBEnable()", "ProTestClassCreate()",
        status, status != PRO_TK_NO_ERROR);
    return (0);
}

/*====================================================================*\
  Function : ProTestCreateIcon
  Purpose  : Creates an external object owned by the current model
\*====================================================================*/
int ProTestCreateIcon(ProAppData app_data, int b)
{
    ProError            status;
    int                 i, transf_num;
    ProMatrix           *transf, comp_trf, end_trf;    
    ProMdl		model = ((AppData *)app_data)->model;
    AppData		tmp_app_data;
    ProSelection	*sels;
    ProModelitem	surface_item;
    ProSurface		surface;
    double		resolution;
    int			nlines[2], num;
    ProMdlType		mdl_type;
    ProLinedata		*mesh_lines;
    ProAsmcomppath	comp_path;
    static ProLinedata arrow[9] =  {    
        {PRO_ENT_LINE, {0.0,0.0,0.0}, {1.0,0.0,0.0}},
        {PRO_ENT_LINE, {1.0,0.0,0.0}, {1.0-HL,HW,0.0}},
        {PRO_ENT_LINE, {1.0,0.0,0.0}, {1.0-HL,0.0,HW}},
        {PRO_ENT_LINE, {1.0,0.0,0.0}, {1.0-HL,-HW,0.0}},
        {PRO_ENT_LINE, {1.0,0.0,0.0}, {1.0-HL,0.0,-HW}},
        {PRO_ENT_LINE, {1.0-HL,HW,0.0}, {1.0-HL,0.0,HW}},
        {PRO_ENT_LINE, {1.0-HL,0.0,HW}, {1.0-HL,-HW,0.0}},
        {PRO_ENT_LINE, {1.0-HL,-HW,0.0}, {1.0-HL,0.0,-HW}},
        {PRO_ENT_LINE, {1.0-HL,0.0,-HW}, {1.0-HL,HW,0.0}}};
    ProLinedata *arrow_lines;
    ProMatrix mesh_transf = {
        {1.0, 0.0, 0.0, 0.0},
        {0.0, 1.0, 0.0, 0.0},
        {0.0, 0.0, 1.0, 0.0},
        {0.0, 0.0, 0.0, 0.0}};

    status = ProArrayAlloc (0, sizeof (ProLinedata), 1,
        (ProArray*)&arrow_lines);
    TEST_CALL_REPORT("ProArrayAlloc()", "ProTestCreateIcon()", status,
	status != PRO_TK_NO_ERROR);
    status = ProArrayObjectAdd ((ProArray*)&arrow_lines, PRO_VALUE_UNUSED,
        9, &arrow[0]);
    TEST_CALL_REPORT("ProArrayObjectAdd()", "ProTestCreateIcon()", status,
	status != PRO_TK_NO_ERROR);
    
    tmp_app_data.fp = ((AppData *)app_data)->fp;
    tmp_app_data.model = model;
    tmp_app_data.lines = NULL;
    tmp_app_data.transf = NULL;
    
    status = ProMdlTypeGet (model, &mdl_type);
    TEST_CALL_REPORT("ProMdlTypeGet()", "ProTestCreateIcon()", status,
	status != PRO_TK_NO_ERROR);
    ProUtilMsgPrint("gen", "TEST %0s", "Select surface for mesh");
    status = ProSelect ((char*)"surface", 1, NULL, NULL, NULL, NULL, &sels, &num);
    if (status != PRO_TK_NO_ERROR || num != 1)
        return status;
    TEST_CALL_REPORT("ProSelect()", "ProTestCreateIcon()", status,
					status != PRO_TK_NO_ERROR);
    if (mdl_type == PRO_ASSEMBLY)
    {
        status = ProSelectionAsmcomppathGet (sels[0], &comp_path);
        status = ProAsmcomppathTrfGet (&comp_path, PRO_B_TRUE, comp_trf);
    }
    status = ProSelectionModelitemGet (sels[0], &surface_item);
    if (status != PRO_TK_NO_ERROR)
        return status;
    TEST_CALL_REPORT("ProSelectionModelitemGet()", "ProTestCreateIcon()", 
        status, status != PRO_TK_NO_ERROR);
    status = ProSurfaceInit (surface_item.owner, surface_item.id, &surface);
    TEST_CALL_REPORT("ProSurfaceInit()", "ProTestCreateIcon()", status,
					status != PRO_TK_NO_ERROR);
    resolution = 0.1;
    nlines[0] = 5;
    nlines[1] = 5;
    ProUtilCollectMeshPoints (&surface,
			resolution,
			nlines,
			ProTestMeshPointsAct,
			(ProAppData)&tmp_app_data);
    resolution = 0.0;
    ProUtilCollectMeshPoints (&surface,
			resolution,
			nlines,
			ProTestArrowPointsAct,
			(ProAppData)&tmp_app_data);
    if (is_mesh_class == 0) 
    {
	ProStringToWstring (mesh_class.name, (char*)"Mesh");
        mesh_class.type = 0;
        ProTestClassCreate (&mesh_class);
        status = ProExtobjClassWarningDisable(&mesh_class, 
            (ProExtobjAction) (PRO_EO_ACT_REF_SUPPR|PRO_EO_ACT_REF_DELETE));
        TEST_CALL_REPORT("ProExtobjClassWarningDisable()", 
            "ProTestCreateIcon()",
            status, status != PRO_TK_NO_ERROR);
        is_mesh_class = 1;
    }
    if (is_arrow_class == 0) 
    {
	ProStringToWstring (arrow_class.name, (char*)"Arrow");
        arrow_class.type = 1;
        ProTestClassCreate (&arrow_class);
        status = ProExtobjClassWarningEnable(&arrow_class, 
            (ProExtobjAction) (PRO_EO_ACT_REF_SUPPR|PRO_EO_ACT_REF_DELETE));
        TEST_CALL_REPORT("ProExtobjClassWarningEnable()", 
            "ProTestCreateIcon()",
            status, status != PRO_TK_NO_ERROR);
	is_arrow_class = 1;
    }			
    
    transf = tmp_app_data.transf;
    mesh_lines = tmp_app_data.lines;
    status = ProArraySizeGet (transf, &transf_num);
    TEST_CALL_REPORT("ProArraySizeGet()", "ProTestCreateIcon()", 
        status, status != PRO_TK_NO_ERROR);
    for (i = 0; i < transf_num; i++)
    {
        if (mdl_type == PRO_ASSEMBLY)
            ProUtilMatrixProduct (transf[i], comp_trf, end_trf);
        else
            ProUtilMatrixCopy (transf[i], end_trf);
        ProTestExtobjCreate (sels[0], &arrow_class, arrow_lines, end_trf);
    }
    if (mdl_type == PRO_ASSEMBLY)
        ProUtilMatrixProduct (comp_trf, mesh_transf, end_trf);
    else
        ProUtilMatrixCopy (mesh_transf, end_trf);
    status = ProArraySizeGet (mesh_lines, &transf_num);
    TEST_CALL_REPORT("ProArraySizeGet()", "ProTestCreateIcon()", 
        status, status != PRO_TK_NO_ERROR);
    ProTestExtobjCreate (sels[0], &mesh_class, mesh_lines, end_trf);
    status = ProArrayFree ((ProArray*)&transf);
    TEST_CALL_REPORT("ProArrayFree()", "ProTestCreateIcon()", 
        status, status != PRO_TK_NO_ERROR);
    status = ProArrayFree ((ProArray*)&mesh_lines);
    TEST_CALL_REPORT("ProArrayFree()", "ProTestCreateIcon()", 
        status, status != PRO_TK_NO_ERROR);
    status = ProWindowRepaint(PRO_VALUE_UNUSED);
    TEST_CALL_REPORT("ProWindowRepaint()", "ProTestCreateIcon()", status,
	status != PRO_TK_NO_ERROR);

    status = ProArrayFree ((ProArray*)&arrow_lines);
    TEST_CALL_REPORT("ProArrayFree()", "ProTestCreateIcon()", status,
	status != PRO_TK_NO_ERROR);
    return (0);
}

/*====================================================================*\
  Function : ProTestDeleteRef
  Purpose  : Delete Ext Obj Reference
\*====================================================================*/
ProError ProTestDeleteRef(
    ProExtobj	*p_extobj,	    /* In : external object */
    ProWExtobjRef ext_obj_ref,      /* In : ext obj reference */
    ProError status,		    /* In : status */
    ProExtobjrefStatus ref_status,  /* In : */
    ProAppData app_data)	    /* In : */
{
    status = ProExtobjRefRemove(p_extobj, ext_obj_ref);
    TEST_CALL_REPORT("ProExtobjRefRemove()", "ProTestDeleteRef()", status,
	status != PRO_TK_NO_ERROR);
    return (PRO_TK_NO_ERROR);
}

/*====================================================================*\
  Function : TestDeleteExtobj
  Purpose  : Delete External Object and all it data
\*====================================================================*/
ProError ProTestDeleteExtobj(
    ProExtobj *p_extobj,    /*In  : external object */
    ProError  status,	    /*In  : status */ 
    ProAppData app_data)    /*In  : app data */
{
    ProWExtobjdata extobjdata;
    ExtobjReference *references;
    int i, references_num;
    ProExtobjClass *class_name = (ProExtobjClass*)app_data;

    status = ProUtilCollectExtobjReferences (p_extobj, &references);
    if (status == PRO_TK_NO_ERROR)
    {
        status = ProArraySizeGet ((ProArray)references, &references_num);
        TEST_CALL_REPORT( "ProArraySizeGet()", "ProTestDeleteExtobj()", 
            status, status != PRO_TK_NO_ERROR );
        for (i = 0; i < references_num; i++)
        {
	    status = ProTestDeleteRef (p_extobj, references[i].p_extobj_ref, 
	        PRO_TK_NO_ERROR, references[i].ref_status, (ProAppData)NULL);
        }
        status = ProArrayFree ((ProArray*)&references);
        TEST_CALL_REPORT( "ProArrayFree()", "ProTestDeleteExtobj()", 
            status, status != PRO_TK_NO_ERROR );
    }
    status = ProExtobjdataGet(p_extobj, class_name, PRO_EXTOBJDAT_DISPLAY,
	&extobjdata);
    TEST_CALL_REPORT("ProExtobjdataGet()", "ProTestDeleteExtobj()", status,
	status != PRO_TK_NO_ERROR);
    status = ProExtobjdataFree(&extobjdata);
    TEST_CALL_REPORT("ProExtobjdataFree()", "ProTestDeleteExtobj()", status,
	status != PRO_TK_NO_ERROR);
    status = ProExtobjdataRemove(p_extobj, class_name, PRO_EXTOBJDAT_SELBOX);
    TEST_CALL_REPORT("ProExtobjdataRemove()", "TestDeleteExtobj()", status,
	status != PRO_TK_NO_ERROR);
    status = ProExtobjDelete(p_extobj, class_name);
    TEST_CALL_REPORT("ProExtobjDelete()", "TestDeleteExtobj()", status,
	status != PRO_TK_NO_ERROR);
 
    return (PRO_TK_NO_ERROR);
}

/*====================================================================*\
  Function : ProTestMenuAct
  Purpose  : Menu action function
\*====================================================================*/
int ProTestMenuAct(ProAppData app_data, int action)
{
    return (ProMenuDeleteWithStatus (action));
}

/*====================================================================*\
  Function : ProTestExtobjClassSelect
  Purpose  : Select extobj class
\*====================================================================*/
ProError ProTestExtobjClassSelect (ProExtobjClass **class_name)
{
    ProError status;
    int menu_id, action;

    status = ProMenuFileRegister((char*)"TkExtobjClass", (char*)"tkextobjclass.mnu", &menu_id);
    TEST_CALL_REPORT("ProMenuFileRegister()", "ProTestExtobjClassSelect()",
        status, status != PRO_TK_NO_ERROR);
    status = ProMenubuttonActionSet((char*)"TkExtobjClass", (char*)"Arrow", 
        (ProMenubuttonAction)ProTestMenuAct,
        &app_data, 1);
    TEST_CALL_REPORT("ProMenubuttonActionSet()", "ProTestExtobjClassSelect()",
        status, status != PRO_TK_NO_ERROR);
    status = ProMenubuttonActionSet((char*)"TkExtobjClass", (char*)"Mesh", 
        (ProMenubuttonAction)ProTestMenuAct,
        &app_data, 0);
    TEST_CALL_REPORT("ProMenubuttonActionSet()", "ProTestExtobjClassSelect()",
        status, status != PRO_TK_NO_ERROR);
    status = ProMenubuttonActionSet((char*)"TkExtobjClass", (char*)"TkExtobjClass", 
        (ProMenubuttonAction)ProMenuDelete, NULL, 0);
    TEST_CALL_REPORT("ProMenubuttonActionSet()", "ProTestExtobjClassSelect()",
        status, status != PRO_TK_NO_ERROR);

    /* Display Menu and set it into action. */
    status = ProMenuCreate(PROMENUTYPE_MAIN, (char*)"TkExtobjClass", &menu_id);
    TEST_CALL_REPORT("ProMenuCreate()", "ProTestExtobjClassSelect()",
        status, status != PRO_TK_NO_ERROR);
    status = ProMenuProcess((char*)"TkExtobjClass", &action);
    TEST_CALL_REPORT("ProMenuProcess()", "ProTestExtobjClassSelect()",
        status, status != PRO_TK_NO_ERROR);
                      
    if (action)
        *class_name = &arrow_class;
    else
        *class_name = &mesh_class;

    return (PRO_TK_NO_ERROR);
}

/*====================================================================*\
  Function : ProTestDeleteAll
  Purpose  : Delete all external objects and delete external obj class
\*====================================================================*/
int ProTestDeleteAll(ProAppData app_data, int b)
{
    ProError status;
    ProMdl model = ((AppData *)app_data)->model;
    ProExtobj	    *extobjs;
    int extobjs_num, i;
    ProExtobjClass *class_name;

    status = ProTestExtobjClassSelect (&class_name);
    if (status != PRO_TK_NO_ERROR)
        return (0);

    status = ProUtilCollectExtobj (model, class_name, &extobjs);
    if (status == PRO_TK_NO_ERROR)
    {
        status = ProArraySizeGet ((ProArray)extobjs, &extobjs_num);
        TEST_CALL_REPORT( "ProArraySizeGet()", "ProTestDeleteAll()", 
            status, status != PRO_TK_NO_ERROR );
        for (i = 0; i < extobjs_num; i++)
        {
            status = ProTestDeleteExtobj (extobjs+i, PRO_TK_NO_ERROR,
	        (ProAppData)class_name);
        }
        status = ProArrayFree ((ProArray*)&extobjs);
        TEST_CALL_REPORT( "ProArrayFree()", "ProTestDeleteAll()", 
            status, status != PRO_TK_NO_ERROR );
    }

    status = ProExtobjClassDelete(class_name);
    TEST_CALL_REPORT("ProExtobjClassDelete()", "ProTestDeleteAll()", status,
	status != PRO_TK_NO_ERROR);
    if (class_name->type == 0)
        is_mesh_class = 0;
    else
        is_arrow_class = 0;
    return (0);
}

/*====================================================================*\
  Function : ProTestDeleteOne
  Purpose  : Delete all external objects and delete external obj class
\*====================================================================*/
int ProTestDeleteOne(ProAppData app_data, int b)
{
    ProError status;
    ProExtobj	    ext_obj;
    int extobjs_num;
    ProSelection *p_sel_list;

    ProUtilMsgPrint("gen", "TEST %0s", "Select an external object");
    status = ProSelect((char*)"ext_obj", 1, NULL, NULL,
                        NULL, NULL, &p_sel_list, &extobjs_num);
    TEST_CALL_REPORT("ProSelect()", "ProTestDeleteOne()", status,
                                                status == PRO_TK_NO_ERROR);
    if( status != PRO_TK_NO_ERROR )
	return(-1);

    status = ProSelectionModelitemGet(p_sel_list[0], (ProExtobj *)&ext_obj);
    TEST_CALL_REPORT("ProSelectionModelitemGet()", "ProTestDeleteOne()",
		    status, status == PRO_TK_NO_ERROR);
    status = ProTestDeleteExtobj (&ext_obj, PRO_TK_NO_ERROR,
	        (ProAppData)NULL/*class*/);
    return (0);
}

/*====================================================================*\
  Function : ProTestAddRef
  Purpose  : Prompts the user to select references for an external object
\*====================================================================*/
ProError ProTestAddRef(ProAppData app_data, int b)
{
    ProError status;
    ProWExtobjRef newRef;
    ProSelection *p_sel_list;
    ProExtobj ext_obj;
    int p_sel_num, i;
    FILE *fp = ((AppData*)app_data)->fp;

    ProUtilMsgPrint("gen", "TEST %0s", "Select an external object");
    status = ProSelect((char*)"ext_obj", 1, NULL, NULL,
                        NULL, NULL, &p_sel_list, &p_sel_num);
    TEST_CALL_REPORT("ProSelect()", "ProtestAddRef()", status,
                                                status == PRO_TK_NO_ERROR);
    if( status != PRO_TK_NO_ERROR )
	   return (PRO_TK_GENERAL_ERROR);

    status = ProSelectionModelitemGet(p_sel_list[0], (ProExtobj *)&ext_obj);
    TEST_CALL_REPORT("ProSelectionModelitemGet()", "ProtestAddRef()",
		    status, status == PRO_TK_NO_ERROR);

    status = ProExtobjRefAlloc(&newRef);
    TEST_CALL_REPORT("ProExtobjRefAlloc()", "ProtestAddRef()", status,
						status != PRO_TK_NO_ERROR);
    
    status = ProExtobjReftypeSet(newRef, 1);
    TEST_CALL_REPORT("ProExtobjReftypeSet()", "ProtestAddRef()", status,
						status != PRO_TK_NO_ERROR);

    status = ProExtobjReftypeGet(newRef, &i);
    TEST_CALL_REPORT("ProExtobjReftypeGet()", "ProtestAddRef()", status,
						status != PRO_TK_NO_ERROR);
    status = ProSelect((char*)"surface", 1, NULL, NULL,
                        NULL, NULL, &p_sel_list, &p_sel_num);
    TEST_CALL_REPORT("ProSelect()", "ProtestAddRef()", status,
                                                status == PRO_TK_NO_ERROR);

    if( status != PRO_TK_NO_ERROR )
	return PRO_TK_GENERAL_ERROR;

    status = ProExtobjRefselectionSet(newRef, p_sel_list[0]);
    TEST_CALL_REPORT("ProExtobjRefselectionSet()", "ProtestAddRef()", status,
						status != PRO_TK_NO_ERROR);

    status = ProExtobjRefAdd(&ext_obj, newRef);
    TEST_CALL_REPORT("ProExtobjRefAdd()", "ProtestAddRef()", status,
						status != PRO_TK_NO_ERROR);

    ProTKFprintf(fp, "Extobj id:\t%d\t Ref type:\t%d\n", ext_obj.id, i);

    status = ProExtobjRefFree(&newRef);
    TEST_CALL_REPORT("ProExtobjRefFree()", "ProtestAddRef()", status,
	status != PRO_TK_NO_ERROR);
    return PRO_TK_NO_ERROR;
}

/*====================================================================*\
  Function : ProTestRefCheck
  Purpose  : Checks to see if the owner of an external object reference
	     has been modified.
\*====================================================================*/
ProError ProTestRefCheck(ProExtobj *object, ProWExtobjRef ext_obj_ref, 
		       ProError in_status, ProExtobjrefStatus ref_status, 
		       ProAppData app_data)
{
    ProWVerstamp stamp1, stamp2, stamp3;
    ProError status;
    int flag;
    ProSelection ref_sel;
    FILE *fp = (FILE *)app_data;
    int i;
    ProModelitem model_item;

    status = ProMdlVerstampGet(object->owner, &stamp1);
    TEST_CALL_REPORT("ProMdlVerstampGet()", "ProTestRefCheck()", status,
						status != PRO_TK_NO_ERROR);
    status = ProExtobjRefselectionGet(object, ext_obj_ref, &ref_sel);
    TEST_CALL_REPORT("ProExtobjRefselectionGet()", "ProTestRefCheck()", status,
						status != PRO_TK_NO_ERROR);
    status = ProSelectionModelitemGet(ref_sel, &model_item);
    TEST_CALL_REPORT("ProSelectionModelitemGet()", "ProTestRefCheck()",
                    status, status != PRO_TK_NO_ERROR);

    i = FindRefIndex(model_item.id);

    if( refVersionTable[i].stamp_str == NULL )
    {
	status = ProVerstampStringGet(stamp1, &(refVersionTable[i].stamp_str));
        TEST_CALL_REPORT("ProVerstampStringGet()", "ProTestRefCheck()", status,
						status != PRO_TK_NO_ERROR);
    }

    status = ProStringVerstampGet(refVersionTable[i].stamp_str, &stamp2);
    TEST_CALL_REPORT("ProStringVerstampGet()", "ProTestRefCheck()", status,
						status != PRO_TK_NO_ERROR);

    flag = ProVerstampEqual(stamp1, stamp2);
    TEST_CALL_REPORT("ProVerstampEqual()", "ProTestRefCheck()", 
	(ProError) (1-flag), flag != 0 && flag != 1);
    
    flag = 0;
    if( flag == 0 )
    {
	status = ProVerstampStringFree(&(refVersionTable[i].stamp_str));
        TEST_CALL_REPORT("ProVerstampStringFree()", "ProTestRefCheck()", status,
						status != PRO_TK_NO_ERROR);
	status = ProVerstampStringGet(stamp1, &(refVersionTable[i].stamp_str));
        TEST_CALL_REPORT("ProVerstampStringGet()", "ProTestRefCheck()", status,
						status != PRO_TK_NO_ERROR);
    }

    status = ProVerstampFree(&stamp1);
    TEST_CALL_REPORT("ProVerstampFree()", "ProTestRefCheck()", status,
						status != PRO_TK_NO_ERROR);
    status = ProVerstampFree(&stamp2);
    TEST_CALL_REPORT("ProVerstampFree()", "ProTestRefCheck()", status,
						status != PRO_TK_NO_ERROR);
    status = ProVerstampAlloc(&stamp3);
    TEST_CALL_REPORT("ProVerstampAlloc()", "ProTestRefCheck()", status,
						status != PRO_TK_NO_ERROR);
    status = ProVerstampFree(&stamp3);
    TEST_CALL_REPORT("ProVerstampFree()", "ProTestRefCheck()", status,
						status != PRO_TK_NO_ERROR);

    ProTKFprintf(fp, "Extobj id:\t%d\t Version stamp unchanged:\t%d\n", 
							object->id, flag);
    return(PRO_TK_NO_ERROR);
}

/*====================================================================*\
  Function : ProTestRefsChanged
  Purpose  : Visits the external object references to determine if they
             have changed.
\*====================================================================*/
ProError ProTestRefsChanged(ProExtobj *extobj, ProError in_status, 
			  ProAppData app_data)
{
    ProError status;
    ExtobjReference *references;
    int i, references_num;

    status = ProUtilCollectExtobjReferences (extobj, &references);
    if (status == PRO_TK_NO_ERROR)
    {
        status = ProArraySizeGet ((ProArray)references, &references_num);
        TEST_CALL_REPORT( "ProArraySizeGet()", "ProTestRefsChanged()", 
            status, status != PRO_TK_NO_ERROR );
        for (i = 0; i < references_num; i++)
        {
            status = ProTestRefCheck (extobj, references[i].p_extobj_ref, 
                PRO_TK_NO_ERROR, references[i].ref_status, (ProAppData)app_data);
        }
        status = ProArrayFree ((ProArray*)&references);
        TEST_CALL_REPORT( "ProArrayFree()", "ProTestRefsChanged()", 
            status, status != PRO_TK_NO_ERROR );
    }

    return(status);
}

/*====================================================================*\
  Function : ProTestExtobjUpdate
  Purpose  : Visits all external objects in the current model and checks
             if references have been changed
\*====================================================================*/
int ProTestExtobjUpdate( ProAppData app_data, int b )
{
    ProMdl model = ((AppData*)app_data)->model;
    ProError status;
    FILE *fp = ((AppData*)app_data)->fp;
    ProExtobj	    *extobjs;
    int extobjs_num, i;

    status = ProUtilCollectExtobj (model, &mesh_class, &extobjs);
    if (status == PRO_TK_NO_ERROR)
    {
        status = ProArraySizeGet ((ProArray)extobjs, &extobjs_num);
        TEST_CALL_REPORT( "ProArraySizeGet()", "ProTestExtobjUpdate()", 
            status, status != PRO_TK_NO_ERROR );
        for (i = 0; i < extobjs_num; i++)
        {
            status = ProTestRefsChanged (extobjs+i, PRO_TK_NO_ERROR,
	        (ProAppData)fp);
        }
        status = ProArrayFree ((ProArray*)&extobjs);
        TEST_CALL_REPORT( "ProArrayFree()", "ProTestExtobjUpdate()", 
            status, status != PRO_TK_NO_ERROR );
    }
    return(0);
}

/*====================================================================*\
  Function : ProTestInfoDispdat
  Purpose  : Write Display data info into file
\*====================================================================*/
void ProTestInfoDispdat(
    ProWExtobjdata disp_data,	/* In : display data */
    FILE *fp)			/* In : file */
{
    int i, num_ents = 0, num_props;
    ProMatrix matr;
    ProColortype color;
    double scale;
    ProLinestyle line_style;
    ProExtobjDispprops *disp_props;
    ProError status;
    ProCurvedata *entity;

    status = ProDispdatEntsGet(disp_data,  &entity, &num_ents);
    TEST_CALL_REPORT("ProDispdatEntsGet()", "ProTestInfoDispdat()", status,
	status != PRO_TK_NO_ERROR);

    if ( status == PRO_TK_NO_ERROR )
    {
	ProTKFprintf(fp, "    Number of entities %d\n", num_ents);

	status = ProCurvedataArrayFree(&entity, num_ents);
	TEST_CALL_REPORT("ProCurvedataArrayFree()", "ProTestInfoDispdat()", 
	    status, status != PRO_TK_NO_ERROR);
    }
    
    status = ProDispdatTrfGet(disp_data, matr);
    TEST_CALL_REPORT("ProDispdatTrfGet()", "ProTestInfoDispdat()", status,
	status != PRO_TK_NO_ERROR);

    status = ProDispdatColorGet(disp_data, &color);
    TEST_CALL_REPORT("ProDispdatColorGet()", "ProTestInfoDispdat()", status,
	status != PRO_TK_NO_ERROR);

    status = ProDispdatScaleGet(disp_data, &scale);
    TEST_CALL_REPORT("ProDispdatScaleGet()", "ProTestInfoDispdat()", status,
	status != PRO_TK_NO_ERROR);

    status = ProDispdatLinestyleGet(disp_data, &line_style);
    TEST_CALL_REPORT("ProDispdatLinestyleGet()", "ProTestInfoDispdat()", status,
	status != PRO_TK_NO_ERROR);

    ProTKFprintf(fp, "    Color %d   Scale %6.3f   Line Style %d\n", color, scale, 
	 line_style);

    status = ProDispdatPropsGet(disp_data, &disp_props, &num_props);
    TEST_CALL_REPORT("ProDispdatPropsGet()", "ProTestInfoDispdat()", status,
	status != PRO_TK_NO_ERROR);

    ProTKFprintf(fp, "    Properties");
    for (i=0; i<num_props; i++)
	ProTKFprintf(fp, "  %d", disp_props[i]);
    ProTKFprintf(fp, "\n");
     
    if (status == PRO_TK_NO_ERROR)
    {
	status = ProArrayFree((ProArray*)&disp_props);
	TEST_CALL_REPORT("ProArrayFree()", "ProTestInfoDispdat()", status,
	    status != PRO_TK_NO_ERROR);
    }
}

/*====================================================================*\
  Function : ProTestInfoSeldat
  Purpose  : Write Selection data info into file
\*====================================================================*/
void ProTestInfoSeldat(
    ProWExtobjdata sel_data,	/* In : display data */
    FILE *fp)			/* In : file */
{
    ProSelbox *selbox;
    int num_box, i;
    ProError status;

    status = ProSeldatSelboxesGet(sel_data, &selbox, &num_box);
    TEST_CALL_REPORT("ProSeldatSelboxesGet()", "ProTestInfoSeldat()", status,
	status != PRO_TK_NO_ERROR);

    for (i=0; i<num_box; i++)
    {
	ProTKFprintf(fp, "    SelBox[%d] %6.2f %6.2f %6.2f    %6.2f %6.2f %6.2f\n",
            i,
	    selbox[i][0][0], selbox[i][0][1], selbox[i][0][2], 
	    selbox[i][1][0], selbox[i][1][1], selbox[i][1][2]);
    }
    
    status = ProArrayFree((ProArray*)&selbox);
    TEST_CALL_REPORT("ProArrayFree()", "ProTestInfoSeldat()", status,
	status != PRO_TK_NO_ERROR);
}

/*====================================================================*\
  Function : ProTestInfoRef
  Purpose  : Write References info into file
\*====================================================================*/
ProError ProTestInfoRef(
    ProExtobj	*p_extobj,	    /* In : external object */
    ProWExtobjRef ext_obj_ref,      /* In : ext obj reference */
    ProError status,		    /* In : status */
    ProExtobjrefStatus ref_status,  /* In : */
    ProAppData app_data)	    /* In : file */
{
    int ref_type;
    ProSelection ref;
    ProModelitem mdlitem;
    FILE *fp = (FILE *)app_data;

    status = ProExtobjReftypeGet(ext_obj_ref, &ref_type);
    TEST_CALL_REPORT("ProExtobjReftypeGet()", "ProTestInfoRef()", status,
	status != PRO_TK_NO_ERROR);

    status = ProExtobjRefselectionGet(p_extobj, ext_obj_ref, &ref);
    TEST_CALL_REPORT("ProExtobjRefselectionGet()", "ProTestInfoRef()", status,
	status != PRO_TK_NO_ERROR);

    status = ProSelectionModelitemGet(ref, &mdlitem);
    TEST_CALL_REPORT("ProSelectionModelitemGet()", "ProTestInfoRef()", status,
	status != PRO_TK_NO_ERROR);

    ProTKFprintf(fp, "        Reference Id %d  Type %d  Status %d\n", mdlitem.id,
	mdlitem.type, ref_status); 

    return (PRO_TK_NO_ERROR);
}

/*====================================================================*\
  Function : ProTestInfoExtobj
  Purpose  : Write External Object info into file
\*====================================================================*/
ProError ProTestInfoExtobj(
    ProExtobj *p_extobj,    /*In  : external object */
    ProError  status,	    /*In  : status */ 
    ProAppData app_data)    /*In  : file */
{
    ProExtobjClass extobjcl;
    char	name[PRO_MDLNAME_SIZE];
    ProModelitem owner;
    ProMdlName w_name;
    ProType type;
    int id, obj_type;
    ProWExtobjdata extobjdata;
    ProMatrix matr_sc;
    FILE *fp = (FILE*)app_data;
    ExtobjReference *references;
    int references_num, i;

    ProTKFprintf(fp, "External Object Id %d\n", p_extobj->id);

    status = ProExtobjClassGet(p_extobj, &extobjcl); 
    TEST_CALL_REPORT("ProExtobjClassGet()", "ProTestInfoExtobj()", status,
	status != PRO_TK_NO_ERROR);
    ProWstringToString(name, extobjcl.name);
    ProTKFprintf(fp, "    Class %20s Type %d\n", name, extobjcl.type);

    status  = ProExtobjOwnerobjGet(p_extobj, &mesh_class, &owner);
    TEST_CALL_REPORT("ProExtobjOwnerobjGet()", "ProTestInfoExtobj()", status,
	status != PRO_TK_NO_ERROR);

    status = ProMdlMdlnameGet(owner.owner, w_name);
    TEST_CALL_REPORT("ProMdlMdlnameGet()", "ProTestInfoExtobj()", status,
	status != PRO_TK_NO_ERROR);

    status = ProMdlTypeGet(owner.owner, (ProMdlType *)&type);
    TEST_CALL_REPORT("ProMdlTypeGet()", "ProTestInfoExtobj()", status,
	status != PRO_TK_NO_ERROR);

    ProWstringToString(name, w_name);
    ProTKFprintf(fp, "    Owner %s Type %d\n", name, type);

    status = ProExtobjExtidGet(p_extobj, &id);
    TEST_CALL_REPORT("ProExtobjExtidGet()", "ProTestInfoExtobj()", status,
	status != PRO_TK_NO_ERROR);

    status = ProExtobjExttypeGet(p_extobj, &obj_type);
    TEST_CALL_REPORT("ProExtobjExttypeGet()", "ProTestInfoExtobj()", status,
	status != PRO_TK_NO_ERROR);

    status = ProExtobjReusableGet(p_extobj);
    TEST_CALL_REPORT("ProExtobjReusableGet()", "ProTestInfoExtobj()", status,
	status != PRO_TK_NO_ERROR && status != PRO_TK_E_NOT_FOUND);
    ProTKFprintf(fp, "    ExtId %6d  ExtType %6d  Reusable is %s\n", id, obj_type, 
	status == PRO_TK_NO_ERROR ? "set" : "not set");

    status = ProExtobjdataGet(p_extobj, &mesh_class, PRO_EXTOBJDAT_DISPLAY,
	&extobjdata);
    TEST_CALL_REPORT("ProExtobjdataGet()", "ProTestInfoExtobj()", status,
	status != PRO_TK_NO_ERROR);
    ProTestInfoDispdat(extobjdata, fp);

    ProExtobjdataFree (&extobjdata);

    status = ProExtobjScreentrfGet(p_extobj, matr_sc);
    TEST_CALL_REPORT("ProExtobjScreentrfGet()", "ProTestInfoExtobj()", status,
	status != PRO_TK_NO_ERROR);

    status = ProExtobjdataGet(p_extobj, &mesh_class, PRO_EXTOBJDAT_SELBOX,
	&extobjdata);
    TEST_CALL_REPORT("ProExtobjdataGet()", "ProTestInfoExtobj()", status,
	status != PRO_TK_NO_ERROR);

    ProTestInfoSeldat(extobjdata, fp);

    ProExtobjdataFree (&extobjdata);
    
    status = ProUtilCollectExtobjReferences (p_extobj, &references);
    if (status == PRO_TK_NO_ERROR)
    {
        status = ProArraySizeGet ((ProArray)references, &references_num);
        TEST_CALL_REPORT( "ProArraySizeGet()", "ProTestInfoExtobj()", 
            status, status != PRO_TK_NO_ERROR );
        for (i = 0; i < references_num; i++)
        {
            status = ProTestInfoRef (p_extobj, references[i].p_extobj_ref, 
                PRO_TK_NO_ERROR, references[i].ref_status, (ProAppData)fp);
        }
        status = ProArrayFree ((ProArray*)&references);
        TEST_CALL_REPORT( "ProArrayFree()", "ProTestInfoExtobj()", 
            status, status != PRO_TK_NO_ERROR );
    }

    return (PRO_TK_NO_ERROR);
}

/*====================================================================*\
  Function : ProTestInfoExtobj
  Purpose  : Visit all external objects in the current model and create
	    info file
\*====================================================================*/
int ProTestInfoAll(ProAppData app_data, int b)
{
    ProError status;
    FILE *fp =  ((AppData*)app_data)->fp;
    ProMdl model = ((AppData*)app_data)->model;
    ProExtobj	    *extobjs;
    int extobjs_num, i;
    
    status = ProUtilCollectExtobj (model, &mesh_class, &extobjs);
    if (status == PRO_TK_NO_ERROR)
    {
        status = ProArraySizeGet ((ProArray)extobjs, &extobjs_num);
        TEST_CALL_REPORT( "ProArraySizeGet()", "ProTestInfoAll()", 
            status, status != PRO_TK_NO_ERROR );
        for (i = 0; i < extobjs_num; i++)
        {
            status = ProTestInfoExtobj(extobjs+i, PRO_TK_NO_ERROR,
	        (ProAppData)fp);
        }
        status = ProArrayFree ((ProArray*)&extobjs);
        TEST_CALL_REPORT( "ProArrayFree()", "ProTestInfoAll()", 
            status, status != PRO_TK_NO_ERROR );
    }

    return (0);
}


/*====================================================================*\
  Function : ProTestExtobj
  Purpose  : Tests the external object functions
\*====================================================================*/
int ProTestExtobj()
{
    ProMdl model;
    ProError status;
    char fname[PRO_NAME_SIZE];
    int menu_id, action;

    status = ProMdlCurrentGet(&model);
    TEST_CALL_REPORT("ProMdlCurrentGet()", "ProTestExtobj()", status,
						status != PRO_TK_NO_ERROR);
    ProTestQcrName(&model, (char *)EXTERNAL_OBJ, fname);
    app_data.fp = PTApplsUnicodeFopen(fname, "a");

    if( app_data.fp == NULL )
	return( -1 );

    status = ProMdlCurrentGet(&app_data.model);
    TEST_CALL_REPORT("ProMdlCurrentGet()", "ProTestExtobj()", status,
						status != PRO_TK_NO_ERROR);

    status = ProMenuFileRegister((char*)"TkExtobj", (char*)"tkextobj.mnu", &menu_id);
    TEST_CALL_REPORT("ProMenuFileRegister()", "ProTestExtobj()",
                      status, status != PRO_TK_NO_ERROR);
    status = ProMenubuttonActionSet((char*)"TkExtobj", (char*)"Create", (ProMenubuttonAction)ProTestCreateIcon,
                                     (ProAppData) &app_data, 0);
    TEST_CALL_REPORT("ProMenubuttonActionSet()", "ProTestExtobj()",
                      status, status != PRO_TK_NO_ERROR);
    status = ProMenubuttonActionSet((char*)"TkExtobj", (char*)"AddReference", (ProMenubuttonAction)ProTestAddRef,
                                     (ProAppData) &app_data, 0);
    TEST_CALL_REPORT("ProMenubuttonActionSet()", "ProTestExtobj()",
                      status, status != PRO_TK_NO_ERROR);
    status = ProMenubuttonActionSet((char*)"TkExtobj", (char*)"UpdateVersion", 
				    (ProMenubuttonAction)ProTestExtobjUpdate, (ProAppData)&app_data, 0);
    TEST_CALL_REPORT("ProMenubuttonActionSet()", "ProTestExtobj()",
                      status, status != PRO_TK_NO_ERROR);
    status = ProMenubuttonActionSet((char*)"TkExtobj", (char*)"Info", 
                    (ProMenubuttonAction)ProTestInfoAll, (ProAppData)&app_data, 0);
    TEST_CALL_REPORT("ProMenubuttonActionSet()", "ProTestExtobj()",
                      status, status != PRO_TK_NO_ERROR);
    status = ProMenubuttonActionSet((char*)"TkExtobj", (char*)"Delete All",
                    (ProMenubuttonAction)ProTestDeleteAll, (ProAppData)&app_data, 0);
    TEST_CALL_REPORT("ProMenubuttonActionSet()", "ProTestExtobj()",
                      status, status != PRO_TK_NO_ERROR);
    status = ProMenubuttonActionSet((char*)"TkExtobj", (char*)"TkExtobj", 
                                    (ProMenubuttonAction)ProMenuDelete, NULL, 0);
    TEST_CALL_REPORT("ProMenubuttonActionSet()", "ProTestExtobj()",
                      status, status != PRO_TK_NO_ERROR);

    /* Display Menu and set it into action. */
    status = ProMenuCreate(PROMENUTYPE_MAIN, (char*)"TkExtobj", &menu_id);
    TEST_CALL_REPORT("ProMenuCreate()", "ProTestExtobj()",
                      status, status != PRO_TK_NO_ERROR);
    status = ProMenuProcess((char*)"TkExtobj", &action);
    TEST_CALL_REPORT("ProMenuProcess()", "ProTestExtobj()",
                      status, status != PRO_TK_NO_ERROR);

    fclose(app_data.fp);
    app_data.fp = NULL;
    if (refVersionTable != NULL)
    {
	free(refVersionTable);
	refVersionTable = NULL;
    }
    return(0);
}