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


/*--------------------------------------------------------------------*\
Pro/TOOLKIT includes
\*--------------------------------------------------------------------*/

#include <ProToolkit.h>
#include <ProAnalysis.h>
#include <ProModelitem.h>
#include <ProMessage.h>
#include <ProArray.h>
#include <ProPoint.h>
#include <ProDtmPnt.h>
#include <ProUtil.h>

/*--------------------------------------------------------------------*\
Application includes
\*--------------------------------------------------------------------*/
#include "TestError.h"
#include "TestAnalysis.h"

#include "UtilMath.h"
#include "UtilMessage.h"
#include "TestDimension.h"

static ProBoolean can_save = PRO_B_FALSE;

/*====================================================================*\
FUNCTION : ProTestPointPtypeGet()
PURPOSE  : Utility to get the placement type of a datum point
\*====================================================================*/
int ProTestPointPtypeGet(
    ProSolid owner,
    ProPoint p,
    ProDtmpntType *ptype)
{
    ProElempathItem items[4];
    ProElempath epath;
    int id;
    ProGeomitem geomitem;
    ProFeature feature;
    ProElement elem_tree, elem;
    int type;
    ProError err = PRO_TK_NO_ERROR;

    ProPointIdGet(p, &id);
    ProModelitemInit(owner, id, PRO_POINT, &geomitem);
    ProGeomitemFeatureGet(&geomitem, &feature);

    items[0].type = PRO_ELEM_PATH_ITEM_TYPE_ID;
    items[0].path_item.elem_id = PRO_E_DTMPNT_PNTS;
    items[1].type = PRO_ELEM_PATH_ITEM_TYPE_INDEX;
    items[1].path_item.elem_index = 0;
    items[2].type = PRO_ELEM_PATH_ITEM_TYPE_ID;
    items[2].path_item.elem_id = PRO_E_DTMPNT_TYPE;
    ProElempathAlloc(&epath);
    ProElempathDataSet(epath, items, 3);

    err = ProFeatureElemtreeExtract (&feature, NULL,
                             PRO_FEAT_EXTRACT_NO_OPTS, &elem_tree );
    if(err != PRO_TK_NO_ERROR)
        return(0);
    err = ProElemtreeElementGet(elem_tree, epath, &elem);
    if(err != PRO_TK_NO_ERROR)
        return(0);
    err = ProElementIntegerGet(elem, NULL, &type);
    if(err != PRO_TK_NO_ERROR)
        return(0);

    ProElempathFree (&epath);
    *ptype = (ProDtmpntType)type;

    return(1);
}

/*====================================================================*\
FUNCTION : ProTestSurfcsysUiAction()
PURPOSE  : Callback for UI action of analysis feature
\*====================================================================*/
ProError ProTestSurfcsysUiAction (
    ProAnalysis analysis)
{
    Surfcsysdata_t *data;
    ProError err = PRO_TK_NO_ERROR;
    ProSelection *sel;
    int n_sel, status;
    ProModelitem modelitem;
    ProPoint p;
    ProDtmpntType ptype;

    TEST_CALL_REPORT ("ProAnalysisUiAction()", 
        "ProTestSurfcsysUiAction()", err, err != PRO_TK_NO_ERROR);

/*--------------------------------------------------------------------*\
    Ask the user to select a datum point on a surface. Check that it
    is on a surface, and reject it if it is not.
\*--------------------------------------------------------------------*/
    ProMessageDisplay (msgfil, (char *)"USER Select a point on a surface");

    while (1)
    {
        if (ProSelect ((char *)"point", 1, NULL, NULL, NULL, NULL, &sel, &n_sel) 
            != PRO_TK_NO_ERROR || n_sel < 0)
            return (PRO_TK_USER_ABORT);

        err = ProSelectionModelitemGet (sel[0], &modelitem);
        TEST_CALL_REPORT ("ProSelectionModelitemGet()", 
            "ProTestSurfcsysUiAction()", err, err != PRO_TK_NO_ERROR);
        err = ProPointInit ((ProSolid)modelitem.owner, modelitem.id, &p);
        TEST_CALL_REPORT ("ProPointInit()", 
            "ProTestSurfcsysUiAction()", err, err != PRO_TK_NO_ERROR);

        status = ProTestPointPtypeGet ((ProSolid)modelitem.owner, p, &ptype);
        if (!status || ptype != PRO_DTMPNT_TYPE_ON_SURF)
        {
            ProMessageDisplay (msgfil,
                (char *)"USER That is not a point on surf. Select again"); 
            continue;
        }

        break;
    }

/*--------------------------------------------------------------------*\
    Get the pointer to the data stored for this analysis feature,
    and set the datum point in it.
\*--------------------------------------------------------------------*/
    err = ProAnalysisInfoGet (analysis, (ProAppData*)&data);
    TEST_CALL_REPORT ("ProAnalysisInfoGet()", 
        "ProTestSurfcsysUiAction()", err, err != PRO_TK_NO_ERROR);
    if (err != PRO_TK_NO_ERROR)
	return(PRO_TK_GENERAL_ERROR);

    err = ProSelectionCopy (sel[0], &data->point);
    TEST_CALL_REPORT ("ProSelectionCopy()", 
        "ProTestSurfcsysUiAction()", err, err != PRO_TK_NO_ERROR);

/*--------------------------------------------------------------------*\
    Ask the user whether the Z axis is inwards or outwards
\*--------------------------------------------------------------------*/
    ProMessageDisplay (msgfil, (char *)"USER Is the Z axis to be outwards? ||| Yes"); 
    data->outwards =  (ProBoolean)ProUtilYesnoGet ((char *)"YES");

/*--------------------------------------------------------------------*\
    Ask the user for the Z offset
\*--------------------------------------------------------------------*/
    ProMessageDisplay (msgfil, (char *)"USER Enter the Z offset distance|||0.0"); 
    err = ProMessageDoubleRead(NULL, &data->offset);

    if (err == PRO_TK_MSG_USER_QUIT)
        return(PRO_TK_USER_ABORT);
    if (err != PRO_TK_NO_ERROR)
        data->offset = 0.0;

    return(PRO_TK_NO_ERROR);
}

/*====================================================================*\
FUNCTION : ProTestSurfcsysDimsAction()
PURPOSE  : Create dimentions
\*====================================================================*/
ProError ProTestSurfcsysDimsAction(
    ProAnalysis analysis,
    double **dims)
{
    Surfcsysdata_t *data;
    ProError err = PRO_TK_NO_ERROR;

    TEST_CALL_REPORT ("ProAnalysisDimsAction()", 
        "ProTestSurfcsysDimsAction()", err, err != PRO_TK_NO_ERROR);

    /* FOR ABORT TESTING */
    ProMessageDisplay (msgfil, (char *)"USER Continue or Abort DimsAction (Y/N)?"); 
    if (ProUtilYesnoGet ((char *)"YES") == PRO_B_FALSE)
        return (PRO_TK_USER_ABORT);
   
    err = ProAnalysisInfoGet(analysis, (ProAppData*)&data);
    TEST_CALL_REPORT ("ProAnalysisInfoGet()", 
        "ProTestSurfcsysDimsAction()", err, err != PRO_TK_NO_ERROR);
    if (err != PRO_TK_NO_ERROR)
	return(PRO_TK_GENERAL_ERROR);
 
    err = ProArrayObjectAdd ((ProArray*)dims, PRO_VALUE_UNUSED, 1, 
        &data->offset);
    TEST_CALL_REPORT ("ProArrayObjectAdd()", 
        "ProTestSurfcsysDimsAction()", err, err != PRO_TK_NO_ERROR);
    
    return(PRO_TK_NO_ERROR);
}

/*====================================================================*\
FUNCTION : ProTestSurfcsysInfoallocAction()
PURPOSE  : Callback to allocate application data needed to describe
		the analysis feature
\*====================================================================*/
ProError ProTestSurfcsysInfoallocAction(
    ProAnalysis analysis)
{
    Surfcsysdata_t *data;
    ProError err = PRO_TK_NO_ERROR;

    TEST_CALL_REPORT ("ProAnalysisInfoallocAction()", 
        "ProTestSurfcsysInfoallocAction()", err, err != PRO_TK_NO_ERROR);

    /* FOR ABORT TESTING */
    ProMessageDisplay (msgfil, 
        (char *)"USER Continue or Abort InfoallocAction (Y/N)?"); 
    if (ProUtilYesnoGet ((char *)"YES") == PRO_B_FALSE)
        return (PRO_TK_USER_ABORT);

/*--------------------------------------------------------------------*\
    Test ProAnalysisInfoGet before info has been set
\*--------------------------------------------------------------------*/
    err = ProAnalysisInfoGet (analysis, (ProAppData*)&data);
    TEST_CALL_REPORT ("ProAnalysisInfoGet()", 
        "ProTestSurfcsysInfoallocAction()", err, 
        (err != PRO_TK_NO_ERROR) || (data != NULL));

/*--------------------------------------------------------------------*\
    Allocate a data structure
\*--------------------------------------------------------------------*/
    data = (Surfcsysdata_t*)calloc(1, sizeof(Surfcsysdata_t));
    if (data == NULL)
        return PRO_TK_USER_ABORT;

/*--------------------------------------------------------------------*\
    Put the data pointer into the analysis
\*--------------------------------------------------------------------*/
    err = ProAnalysisInfoSet(analysis, data);
    TEST_CALL_REPORT ("ProAnalysisInfoSet()", 
        "ProTestSurfcsysInfoallocAction()", err, err != PRO_TK_NO_ERROR);

    return(PRO_TK_NO_ERROR);
}

/*====================================================================*\
FUNCTION : ProTestSurfcsysInfofreeAction()
PURPOSE  : Callback to free the application data
\*====================================================================*/
ProError ProTestSurfcsysInfofreeAction(
    ProAnalysis analysis)
{
    Surfcsysdata_t *data;
    ProError err = PRO_TK_NO_ERROR;

    TEST_CALL_REPORT ("ProAnalysisInfofreeAction()", 
        "ProTestSurfcsysInfofreeAction()", err, err != PRO_TK_NO_ERROR);
/*--------------------------------------------------------------------*\
    Get the data pointer from the analysis
\*--------------------------------------------------------------------*/
    err = ProAnalysisInfoGet (analysis, (ProAppData*)&data);
    TEST_CALL_REPORT ("ProAnalysisInfoGet()", 
        "ProTestSurfcsysInfofreeAction()", err, err != PRO_TK_NO_ERROR);
    if (err != PRO_TK_NO_ERROR)
        return(PRO_TK_GENERAL_ERROR);

/*--------------------------------------------------------------------*\
    Free the data
\*--------------------------------------------------------------------*/
    free (data);

/*--------------------------------------------------------------------*\
    Set the data pointer in the analysis to NULL
\*--------------------------------------------------------------------*/
    err = ProAnalysisInfoSet (analysis, NULL);
    TEST_CALL_REPORT ("ProAnalysisInfoSet()", 
        "ProTestSurfcsysInfofreeAction()", err, err != PRO_TK_NO_ERROR);

    return(PRO_TK_NO_ERROR);
}

/*====================================================================*\
FUNCTION : ProTestSurfcsysCompcheckAction()
PURPOSE  : Callback to tell Pro/E whether computation can be performed
		in this analysis feature
\*====================================================================*/
ProError ProTestSurfcsysCompcheckAction(
    ProAnalysis analysis)
{
    Surfcsysdata_t *data;
    ProError err = PRO_TK_NO_ERROR;

    TEST_CALL_REPORT ("ProAnalysisComputecheckAction()", 
        "ProTestSurfcsysCompcheckAction()", err, err != PRO_TK_NO_ERROR);

    /* FOR ABORT TESTING */
    ProMessageDisplay (msgfil, 
        (char *)"USER Continue or Abort CompcheckAction (Y/N)?"); 
    if (ProUtilYesnoGet ((char *)"YES") == PRO_B_FALSE)
        return (PRO_TK_USER_ABORT);
/*--------------------------------------------------------------------*\
    Get the analysis data
\*--------------------------------------------------------------------*/
    err = ProAnalysisInfoGet (analysis, (ProAppData*)&data);
    TEST_CALL_REPORT ("ProAnalysisInfoGet()", 
        "ProTestSurfcsysCompcheckAction()", err, err != PRO_TK_NO_ERROR);
    if (err != PRO_TK_NO_ERROR)
        return(PRO_TK_GENERAL_ERROR);

/*--------------------------------------------------------------------*\
    Return NO_ERROR if the datum point selection is set OK
\*--------------------------------------------------------------------*/
    return (data->point!=NULL?PRO_TK_NO_ERROR:PRO_TK_USER_ABORT);
}

/*====================================================================*\
FUNCTION : ProTestPointSrfGet()
PURPOSE  : Utility to get the surface a datum point is placed on.
\*====================================================================*/
int ProTestPointSrfGet(
    ProSelection point,
    ProSurface *surface)
{
    ProElempathItem items[4];
    ProElempath epath;
    int id;
    ProGeomitem geomitem;
    ProFeature feature;
    ProSelection sel;
    ProElement elem_tree, elem;
    ProReference ref;
    ProError err = PRO_TK_NO_ERROR;

    ProSelectionModelitemGet(point, &geomitem);
    ProGeomitemFeatureGet(&geomitem, &feature);

    items[0].type = PRO_ELEM_PATH_ITEM_TYPE_ID;
    items[0].path_item.elem_id = PRO_E_DTMPNT_PNTS;
    items[1].type = PRO_ELEM_PATH_ITEM_TYPE_INDEX;
    items[1].path_item.elem_index = 0;
    items[2].type = PRO_ELEM_PATH_ITEM_TYPE_ID;
    items[2].path_item.elem_id = PRO_E_DTMPNT_PLACE_SURF;
    ProElempathAlloc(&epath);
    ProElempathDataSet(epath, items, 3);

    err = ProFeatureElemtreeExtract (&feature, NULL,
                             PRO_FEAT_EXTRACT_NO_OPTS, &elem_tree );
    if(err != PRO_TK_NO_ERROR)
        return(0);
    err = ProElemtreeElementGet(elem_tree, epath, &elem);
    if(err != PRO_TK_NO_ERROR)
        return(0);
    err = ProElementReferenceGet(elem, NULL, &ref);
    if(err != PRO_TK_NO_ERROR)
        return(0);
    err = ProReferenceToSelection(ref, &sel);
    if(err != PRO_TK_NO_ERROR)
        return(0);

    err = ProReferenceFree( ref );

    ProSelectionModelitemGet(sel, &geomitem);
    ProSurfaceInit(geomitem.owner, geomitem.id, surface);

    return(1);
}

/*====================================================================*\
FUNCTION : ProTestSurfcsysComputeAction()
PURPOSE  : Callback to perform the analysis feature computation
\*====================================================================*/
ProError ProTestSurfcsysComputeAction(
    ProAnalysis analysis)
{
    Surfcsysdata_t *data;
    ProModelitem modelitem;
    ProPoint p;
    ProVector pos;
    ProSurface surface;
    ProUvParam uv;
    ProVector normal, der1[2];
    ProFeature feature;
    ProCharName name;
    ProError err = PRO_TK_NO_ERROR;

    TEST_CALL_REPORT ("ProAnalysisComputeAction()", 
        "ProTestSurfcsysComputeAction()", err, err != PRO_TK_NO_ERROR);
/*--------------------------------------------------------------------*\
    Get the application data from the analysis
\*--------------------------------------------------------------------*/
    err = ProAnalysisInfoGet (analysis, (ProAppData*)&data);
    TEST_CALL_REPORT ("ProAnalysisInfoGet()", 
        "ProTestSurfcsysComputeAction()", err, err != PRO_TK_NO_ERROR);
    if (err != PRO_TK_NO_ERROR)
	return(PRO_TK_GENERAL_ERROR);

/*--------------------------------------------------------------------*\
    Get the location of the datum point
\*--------------------------------------------------------------------*/
    err = ProSelectionModelitemGet (data->point, &modelitem);
    TEST_CALL_REPORT ("ProSelectionModelitemGet()", 
        "ProTestSurfcsysComputeAction()", err, err != PRO_TK_NO_ERROR);
    
    err = ProPointInit ((ProSolid)modelitem.owner, modelitem.id, &p);
    TEST_CALL_REPORT ("ProPointInit()", 
        "ProTestSurfcsysComputeAction()", err, err != PRO_TK_NO_ERROR);
    
    err = ProPointCoordGet (p, data->csysdata.origin);
    TEST_CALL_REPORT ("ProPointCoordGet()", 
        "ProTestSurfcsysComputeAction()", err, err != PRO_TK_NO_ERROR);

/*--------------------------------------------------------------------*\
    Find the surface the datum point sits on, and find its first
    derivative vectors and the normal
\*--------------------------------------------------------------------*/
    ProTestPointSrfGet (data->point, &surface);

    err = ProSurfaceParamEval ((ProSolid)modelitem.owner, surface, 
        data->csysdata.origin, uv);
    TEST_CALL_REPORT ("ProSurfaceParamEval()", 
        "ProTestSurfcsysComputeAction()", err, err != PRO_TK_NO_ERROR);

    err = ProSurfaceXyzdataEval (surface, uv, NULL, der1, NULL, normal);
    TEST_CALL_REPORT ("ProSurfaceXyzdataEval()", 
        "ProTestSurfcsysComputeAction()", err, err != PRO_TK_NO_ERROR);

/*--------------------------------------------------------------------*\
    Flip and offset if necessary
\*--------------------------------------------------------------------*/
    if(!data->outwards)
        ProUtilVectorScale (-1.0, normal, normal);

    ProUtilVectorsLincom (1.0, data->csysdata.origin, data->offset, normal,
        data->csysdata.origin);

/*--------------------------------------------------------------------*\
    Copy this information to the application data as a csys geometry
\*--------------------------------------------------------------------*/
    ProUtilVectorNormalize(der1[0], data->csysdata.x_vector);
    ProUtilVectorNormalize(normal, data->csysdata.z_vector);
    ProUtilVectorCross(data->csysdata.z_vector,
			data->csysdata.x_vector, data->csysdata.y_vector);
/*--------------------------------------------------------------------*\
    can_save flag will be checked in Savecheck Action
\*--------------------------------------------------------------------*/
    can_save = PRO_B_TRUE;

    return(PRO_TK_NO_ERROR);
}

/*====================================================================*\
FUNCTION : ProTestSurfcsysDisplayAction()
PURPOSE  : Callback to display the results of the analysis computation
\*====================================================================*/
ProError ProTestSurfcsysDisplayAction(
    ProAnalysis analysis)
{
    Surfcsysdata_t *data;
    ProVector pos;
    ProError err = PRO_TK_NO_ERROR;

    TEST_CALL_REPORT ("ProAnalysisDisplayAction()", 
        "ProTestSurfcsysDisplayAction()", err, err != PRO_TK_NO_ERROR);
/*--------------------------------------------------------------------*\
    Get the application data from the analysis
\*--------------------------------------------------------------------*/
    err = ProAnalysisInfoGet (analysis, (ProAppData*)&data);
    TEST_CALL_REPORT ("ProAnalysisInfoGet()", 
        "ProTestSurfcsysDisplayAction()", err, err != PRO_TK_NO_ERROR);
    if (err != PRO_TK_NO_ERROR)
	return(PRO_TK_GENERAL_ERROR);

/*--------------------------------------------------------------------*\
    Draw a line for the X vector
\*--------------------------------------------------------------------*/
    ProGraphicsPenPosition(data->csysdata.origin);
    pos[0]=data->csysdata.origin[0]+data->csysdata.x_vector[0];
    pos[1]=data->csysdata.origin[1]+data->csysdata.x_vector[1];
    pos[2]=data->csysdata.origin[2]+data->csysdata.x_vector[2];
    ProGraphicsLineDraw(pos);

/*--------------------------------------------------------------------*\
    Draw a line for the Y vector
\*--------------------------------------------------------------------*/
    ProGraphicsPenPosition(data->csysdata.origin);
    pos[0]=data->csysdata.origin[0]+data->csysdata.y_vector[0];
    pos[1]=data->csysdata.origin[1]+data->csysdata.y_vector[1];
    pos[2]=data->csysdata.origin[2]+data->csysdata.y_vector[2];
    ProGraphicsLineDraw(pos);

/*--------------------------------------------------------------------*\
    Draw a line for the Z vector
\*--------------------------------------------------------------------*/
    ProGraphicsPenPosition(data->csysdata.origin);
    pos[0]=data->csysdata.origin[0]+data->csysdata.z_vector[0];
    pos[1]=data->csysdata.origin[1]+data->csysdata.z_vector[1];
    pos[2]=data->csysdata.origin[2]+data->csysdata.z_vector[2];
    ProGraphicsLineDraw(pos);

    return(PRO_TK_NO_ERROR);
}

/*====================================================================*\
FUNCTION : ProTestSurfcsysOutputAction()
PURPOSE  : Callback to write textual information about the result of
		the computation
\*====================================================================*/
ProError ProTestSurfcsysOutputAction(
    ProAnalysis analysis,
    ProLine **lines)
{
    Surfcsysdata_t *data;
    ProCharLine buff;
    ProLine ll[4];
    ProError err = PRO_TK_NO_ERROR;

    TEST_CALL_REPORT ("ProAnalysisOutputAction()", 
        "ProTestSurfcsysOutputAction()", err, err != PRO_TK_NO_ERROR);
/*--------------------------------------------------------------------*\
    Get the application dara from the analysis
\*--------------------------------------------------------------------*/
    err = ProAnalysisInfoGet (analysis, (ProAppData*)&data);
    TEST_CALL_REPORT ("ProAnalysisInfoGet()", 
        "ProTestSurfcsysOutputAction()", err, err != PRO_TK_NO_ERROR);
    if (err != PRO_TK_NO_ERROR)
	return(PRO_TK_GENERAL_ERROR);

/*--------------------------------------------------------------------*\
    Write the origin of the csys
\*--------------------------------------------------------------------*/
    ProTKSprintf(buff,"Csys is at location (%0.2f, %0.2f, %0.2f)",
	    data->csysdata.origin[0],
	    data->csysdata.origin[1],
	    data->csysdata.origin[2]);
    ProStringToWstring(ll[0], buff);

/*--------------------------------------------------------------------*\
     X vector
\*--------------------------------------------------------------------*/
    ProTKSprintf(buff,"           X vector (%0.2f, %0.2f, %0.2f)",
	    data->csysdata.x_vector[0],
	    data->csysdata.x_vector[1],
	    data->csysdata.x_vector[2]);
    ProStringToWstring(ll[1], buff);

/*--------------------------------------------------------------------*\
     Y vector
\*--------------------------------------------------------------------*/
    ProTKSprintf(buff,"           Y vector (%0.2f, %0.2f, %0.2f)",
	    data->csysdata.y_vector[0],
	    data->csysdata.y_vector[1],
	    data->csysdata.y_vector[2]);
    ProStringToWstring(ll[2], buff);

/*--------------------------------------------------------------------*\
     Z vector
\*--------------------------------------------------------------------*/
    ProTKSprintf(buff,"           Z vector (%0.2f, %0.2f, %0.2f)",
	    data->csysdata.z_vector[0],
	    data->csysdata.z_vector[1],
	    data->csysdata.z_vector[2]);
    ProStringToWstring(ll[3], buff);

    err = ProArrayObjectAdd ((ProArray*)lines, PRO_VALUE_UNUSED, 4, ll);
    TEST_CALL_REPORT ("ProArrayObjectAdd()", 
        "ProTestSurfcsysOutputAction()", err, err != PRO_TK_NO_ERROR);

    return(PRO_TK_NO_ERROR);
}

/*====================================================================*\
FUNCTION : ProTestSurfcsysSavecheckAction()
PURPOSE  : Callback to tell Pro/E whether the analysis application
		data can be saved
\*====================================================================*/
ProError ProTestSurfcsysSavecheckAction(
    ProAnalysis analysis)
{
    ProError err = PRO_TK_NO_ERROR;
    
    TEST_CALL_REPORT ("ProAnalysisSavecheckAction()", 
        "ProTestSurfcsysSavecheckAction()", err, err != PRO_TK_NO_ERROR);

    /* FOR ABORT TESTING */
    ProMessageDisplay (msgfil, 
        (char *)"USER Continue or Abort SavecheckAction (Y/N)?"); 
    if (ProUtilYesnoGet ((char *)"YES") == PRO_B_FALSE)
        return (PRO_TK_USER_ABORT);

    return (PRO_TK_NO_ERROR);
}

/*====================================================================*\
FUNCTION : ProTestSurfcsysInfosaveAction()
PURPOSE  : Callback to tell Pro/E what element references are required
		by the analysis feature, and save the rest of the info
		to Ext Data
\*====================================================================*/
ProError ProTestSurfcsysInfosaveAction(
    ProAnalysis analysis,
    ProFeature  *feature,
    ProSelection **references)
{
    Surfcsysdata_t *data;
    ProSelection reference;
    ProError err = PRO_TK_NO_ERROR;

    TEST_CALL_REPORT ("ProAnalysisInfosaveAction()", 
        "ProTestSurfcsysInfosaveAction()", err, err != PRO_TK_NO_ERROR);

    /* FOR ABORT TESTING */
    ProMessageDisplay (msgfil, 
        (char *)"USER Continue or Abort InfosaveAction (Y/N)?"); 
    if (ProUtilYesnoGet ((char *)"YES") == PRO_B_FALSE)
        return (PRO_TK_USER_ABORT);

/*--------------------------------------------------------------------*\
    Get the application data from the analysis
\*--------------------------------------------------------------------*/
    err = ProAnalysisInfoGet (analysis, (ProAppData*)&data);
    TEST_CALL_REPORT ("ProAnalysisInfoGet()", 
        "ProTestSurfcsysInfosaveAction()", err, err != PRO_TK_NO_ERROR);
    if (err != PRO_TK_NO_ERROR)
        return(PRO_TK_GENERAL_ERROR);

/*--------------------------------------------------------------------*\
    Output the reference to the datum point
\*--------------------------------------------------------------------*/
    err = ProSelectionCopy (data->point, &reference);
    TEST_CALL_REPORT ("ProSelectionCopy()", 
        "ProTestSurfcsysInfosaveAction()", err, err != PRO_TK_NO_ERROR);

    err = ProArrayObjectAdd ((ProArray*)references, PRO_VALUE_UNUSED, 1, 
        &reference);
    TEST_CALL_REPORT ("ProArrayObjectAdd()", 
        "ProTestSurfcsysInfosaveAction()", err, err != PRO_TK_NO_ERROR);

/*--------------------------------------------------------------------*\
    Save the "outwards" option
\*--------------------------------------------------------------------*/
    if(!ProTestOutwardsSave (feature, data->outwards))
	return(PRO_TK_GENERAL_ERROR);

/*--------------------------------------------------------------------*\
    Reset can_save flag
\*--------------------------------------------------------------------*/
    can_save = PRO_B_FALSE;

    return(PRO_TK_NO_ERROR);
}

/*====================================================================*\
FUNCTION : ProTestSurfcsysInforetrieveAction()
PURPOSE  : Callback to get the references Pro/E has stored with the
		analysis feature, and retrieve the rest from Ext Data
\*====================================================================*/
ProError ProTestSurfcsysInforetrieveAction(
    ProAnalysis analysis,
    ProFeature *feature,
    ProSelection *references)
{
    Surfcsysdata_t *data;
    int n_dims;
    ProDimension *p_dims;
    ProError err = PRO_TK_NO_ERROR;

    TEST_CALL_REPORT ("ProAnalysisInforetrieveAction()", 
        "ProTestSurfcsysInforetrieveAction()", 
        err, err != PRO_TK_NO_ERROR);
/*--------------------------------------------------------------------*\
    Get the application data from the analysis
\*--------------------------------------------------------------------*/
    err = ProAnalysisInfoGet (analysis, (ProAppData*)&data);
    TEST_CALL_REPORT ("ProAnalysisInfoGet()", 
        "ProTestSurfcsysInforetrieveAction()", 
        err, err != PRO_TK_NO_ERROR);
    if (err != PRO_TK_NO_ERROR)
	return(PRO_TK_GENERAL_ERROR);

/*--------------------------------------------------------------------*\
    Copy the first reference to the application as the datum point
\*--------------------------------------------------------------------*/
    err = ProSelectionCopy (references[0], &data->point);
    TEST_CALL_REPORT ("ProSelectionCopy()", 
        "ProTestSurfcsysInforetrieveAction()", 
        err, err != PRO_TK_NO_ERROR);

/*--------------------------------------------------------------------*\
    Retrieve the "outwards" option from Ext Data
\*--------------------------------------------------------------------*/
    ProTestOutwardsRetrieve(feature, &data->outwards);

/*--------------------------------------------------------------------*\
    If the feature has a dimension, take its value as the offset
\*--------------------------------------------------------------------*/
    err = ProTestFeatureDimensionsCollect (feature, &p_dims);
    err = ProArraySizeGet ((ProArray)p_dims, &n_dims);

    if(n_dims > 0)
    {
        double dim_value;
        err = ProDimensionValueGet(&p_dims[0], &dim_value);
        data->offset = dim_value;
    }

    return(PRO_TK_NO_ERROR);
}

/*====================================================================*\
FUNCTION : ProTestSurfcsysInfocopyAction()
PURPOSE  : 
\*====================================================================*/
ProError ProTestSurfcsysInfocopyAction(
    ProAnalysis from,
    ProAnalysis to)
{
    Surfcsysdata_t *fdata, *tdata;
    ProError err = PRO_TK_NO_ERROR;

    TEST_CALL_REPORT ("ProAnalysisInfocopyAction()", 
        "ProTestSurfcsysInfocopyAction()", 
        err, err != PRO_TK_NO_ERROR);

/*--------------------------------------------------------------------*\
    Get the application data from the analyses
\*--------------------------------------------------------------------*/
    err = ProAnalysisInfoGet (from, (ProAppData*)&fdata);
    TEST_CALL_REPORT ("ProAnalysisInfoGet()", 
        "ProTestSurfcsysInfocopyAction()", 
        err, err != PRO_TK_NO_ERROR);
    if (err != PRO_TK_NO_ERROR)
	return(PRO_TK_GENERAL_ERROR);
    
    err = ProAnalysisInfoGet (to,   (ProAppData*)&tdata);
    TEST_CALL_REPORT ("ProAnalysisInfoGet()", 
        "ProTestSurfcsysInfocopyAction()", 
        err, err != PRO_TK_NO_ERROR);
    if (err != PRO_TK_NO_ERROR)
	return(PRO_TK_GENERAL_ERROR);

/*--------------------------------------------------------------------*\
    Copy the application-specific data (everything except references)
\*--------------------------------------------------------------------*/
    tdata->outwards = fdata->outwards;
    tdata->offset = fdata->offset;

    return(PRO_TK_NO_ERROR);
}

/*====================================================================*\
FUNCTION : ProTestSurfcsysResultAction()
PURPOSE  : Callback to give Pro/E the feature parameters and geometry
		that the analysis feature must contain
\*====================================================================*/
ProError ProTestSurfcsysResultAction(
    ProAnalysis analysis,
    ProBoolean names_only,
    ProAnalysisParameter **parameters,
    ProAnalysisGeomitem  **geometry)
{
    Surfcsysdata_t *data;
    ProAnalysisGeomitem geomitem;
    ProAnalysisParameter param;
    ProError err = PRO_TK_NO_ERROR;

    TEST_CALL_REPORT ("ProAnalysisResultAction()", 
        "ProTestSurfcsysResultAction()", 
        err, err != PRO_TK_NO_ERROR);

/*--------------------------------------------------------------------*\
    Get the application data from the analysis
\*--------------------------------------------------------------------*/
    err = ProAnalysisInfoGet (analysis, (ProAppData*)&data);
    if (err  != PRO_TK_NO_ERROR)
	return(PRO_TK_GENERAL_ERROR);

/*--------------------------------------------------------------------*\
    TEMP PARAMETER JUST TO MAKE SURE THE ENTITY GETS CREATED
\*--------------------------------------------------------------------*/
    ProStringToWstring(param.name, (char *)"TESTPARAM");
    param.create = PRO_B_TRUE;
    ProStringToWstring(param.description, (char *)"This is my test parameter");
    ProArrayAlloc(1, sizeof(ProParamvalue), 1, (ProArray*)&param.values);
    param.values[0].type = PRO_PARAM_DOUBLE;
    param.values[0].value.d_val=99.9;
    ProArrayObjectAdd((ProArray*)parameters, -1, 1, &param);

/*--------------------------------------------------------------------*\
    Specify a CSYS DATUM with the geometry stored in the application data
\*--------------------------------------------------------------------*/
    ProStringToWstring(geomitem.name,(char *)"SURF_CSYS");
    geomitem.create = PRO_B_TRUE;
    geomitem.type = PRO_ANALYSIS_CSYS;
    if (names_only == PRO_B_FALSE)
    {
        ProArrayAlloc(1, sizeof(ProAnalysisEntity), 1, 
            (ProArray*)&geomitem.shapes);
        memcpy(geomitem.shapes[0].csys.origin,   data->csysdata.origin,   
            sizeof(ProVector));
        memcpy(geomitem.shapes[0].csys.x_vector, data->csysdata.x_vector, 
            sizeof(ProVector));
        memcpy(geomitem.shapes[0].csys.y_vector, data->csysdata.y_vector, 
            sizeof(ProVector));
        memcpy(geomitem.shapes[0].csys.z_vector, data->csysdata.z_vector, 
            sizeof(ProVector));
    }

/*--------------------------------------------------------------------*\
    Output the csys geometry description
\*--------------------------------------------------------------------*/
    ProArrayObjectAdd((ProArray*)geometry, PRO_VALUE_UNUSED, 1, 
        &geomitem);

    return(PRO_TK_NO_ERROR);
}

/*====================================================================*\
FUNCTION : ProTestSurfpointResultAction()
PURPOSE  : Callback to give Pro/E the feature parameters and geometry
		that the analysis feature must contain
\*====================================================================*/
ProError ProTestSurfpointResultAction(
    ProAnalysis analysis,
    ProBoolean names_only,
    ProAnalysisParameter **parameters,
    ProAnalysisGeomitem  **geometry)
{
    Surfcsysdata_t *data;
    ProAnalysisGeomitem geomitem;
    ProAnalysisParameter param;
    ProError err = PRO_TK_NO_ERROR;

    ProSurface surface;
    ProGeomitemdata *p_srf_data;

    TEST_CALL_REPORT ("ProAnalysisResultAction()", 
        "ProTestSurfpointResultAction()", 
        err, err != PRO_TK_NO_ERROR);

/*--------------------------------------------------------------------*\
    Get the application data from the analysis
\*--------------------------------------------------------------------*/
    err = ProAnalysisInfoGet (analysis, (ProAppData*)&data);
    TEST_CALL_REPORT ("ProAnalysisInfoGet()",
        "ProTestSurfpointResultAction()", err, err != PRO_TK_NO_ERROR);
    if (err != PRO_TK_NO_ERROR)
	return(PRO_TK_GENERAL_ERROR);

/* DATUM POINT */

    ProStringToWstring(geomitem.name,(char *)"SURF_POINT");
    geomitem.create = PRO_B_TRUE;
    geomitem.type = PRO_ANALYSIS_POINT;

    if (names_only == PRO_B_FALSE)
    {
        ProArrayAlloc (1, sizeof (ProAnalysisEntity), 1, (ProArray*)
            &geomitem.shapes);
        ProCurvedataAlloc (&geomitem.shapes[0].curve);
        TEST_CALL_REPORT ("ProCurvedataAlloc()",
            "ProTestSurfpointResultAction()", err, err != PRO_TK_NO_ERROR);
        err = ProPointdataInit (data->csysdata.origin, 
            geomitem.shapes[0].curve);
        TEST_CALL_REPORT ("ProPointdataInit()",
            "ProTestSurfpointResultAction()", err, err != PRO_TK_NO_ERROR);
        /*geomitem.shapes[0].curve.point.type = PRO_ENT_POINT;
        memcpy(geomitem.shapes[0].curve->point.position,   
            data->csysdata.origin,   sizeof(ProVector));*/
    }

/*--------------------------------------------------------------------*\
    Output the csys geometry description
\*--------------------------------------------------------------------*/
    err = ProArrayObjectAdd((ProArray*)geometry, PRO_VALUE_UNUSED, 
        1, &geomitem);
    TEST_CALL_REPORT ("ProArrayObjectAdd()",
        "ProTestSurfpointResultAction()", err, err != PRO_TK_NO_ERROR);

    return(PRO_TK_NO_ERROR);
}