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

#include "ProToolkit.h"
#include "ProFeature.h"
#include "ProAnalysis.h"
#include "ProArray.h"
#include "ProDtmPnt.h"
#include "ProElement.h"
#include "ProExtdata.h"
#include "ProMessage.h"
#include "ProPoint.h"
#include "ProSection.h"
#include "Pro2dEntdef.h"
#include "ProSelection.h"
#include "ProSurface.h"
#include "ProGraphic.h"
#include "ProModelitem.h"
#include "ProUtil.h"

#include <PTApplsUnicodeUtils.h>
#include <UtilString.h>

#include "UtilMath.h"

static ProFileName msgfil;

/*--------------------------------------------------------------------*\
    Data structure to describe the information needed by an analysis
    feature that creates a surface sys. The csys is centered at a
    datum point on a surface, with its X and Y axes aligned with the
    U and V mesh lines. The Z axis can be inwards or outwards, by
    user choice, and the origin can be offset from the datum point
    by an offset value that is stored as a feature dimension.
\*--------------------------------------------------------------------*/
typedef struct surf_csys_data
{
    /* References */
    ProSelection point;   /* The datum point referenced by the csys */

    /* Dimensions */
    double offset;

    /* Application-specific data */
    ProBoolean outwards;

    /* Results storage */
    ProCsysdata csysdata; /* The geometry of the csys */

} Surfcsysdata_t;

int UsrClassRegister(ProMdl model, ProExtdataClass *class);
/*====================================================================*\
FUNCTION : UsrSurfcsysUiAction()
PURPOSE  : Callback for UI action for surface csys
\*====================================================================*/
ProError UsrSurfcsysUiAction(
    ProAnalysis analysis)
{
    Surfcsysdata_t *data;
    ProError status;
    ProSelection *sel;
    int n_sel;
    ProModelitem modelitem;
    ProPoint p;
    ProDtmpntType ptype;
    
    ProError UsrYesnoGet();

/*--------------------------------------------------------------------*\
    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,"USER Select a point on a surface");
    while(1)
    {
        if(ProSelect("point",1,NULL,NULL,NULL,NULL,&sel,&n_sel)
                != PRO_TK_NO_ERROR || n_sel < 0)
            return(PRO_TK_MSG_USER_QUIT);
        ProSelectionModelitemGet(sel[0], &modelitem);
        ProPointInit(modelitem.owner, modelitem.id, &p);
        if(!UsrPointPtypeGet(modelitem.owner, p, &ptype) ||
                        ptype != PRO_DTMPNT_TYPE_ON_SURF)
        {
            ProMessageDisplay(msgfil,"USER That is not a point on surf. Select again"); 
            continue;
        }
        break;
    }

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

    ProSelectionCopy(sel[0], &data->point);

/*--------------------------------------------------------------------*\
    Ask the user whether the Z axis is inwards or outwards, and
    set the answer in the application data.
\*--------------------------------------------------------------------*/
    ProMessageDisplay(msgfil,"USER Is the Z axis to be outwards? ||| Yes"); 

    if(UsrYesnoGet(msgfil,"YES",&data->outwards) != PRO_TK_NO_ERROR)
        return(PRO_TK_GENERAL_ERROR);

/*--------------------------------------------------------------------*\
    Ask the user for the Z offset, and store that, too.
\*--------------------------------------------------------------------*/
    ProMessageDisplay(msgfil,"USER Enter the Z offset distance|||0.0"); 
    status = ProMessageDoubleRead(NULL, &data->offset);
    if(status == PRO_TK_MSG_USER_QUIT)
        return(PRO_TK_GENERAL_ERROR);

    if(status != PRO_TK_NO_ERROR)
        data->offset = 0.0;

    return(PRO_TK_NO_ERROR);
}

/*====================================================================*\
FUNCTION : UsrSurfcsysDimsAction()
PURPOSE  : Callback for Dims action for surface csys
\*====================================================================*/
ProError UsrSurfcsysDimsAction(
    ProAnalysis analysis,
    double **dims)
{
    Surfcsysdata_t *data;

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

/*--------------------------------------------------------------------*\
    Add the csys offset value as a feature dimension
\*--------------------------------------------------------------------*/
    ProArrayObjectAdd((ProArray*)dims, -1, 1, &data->offset);

    return(PRO_TK_NO_ERROR);
}

/*====================================================================*\
FUNCTION : UsrSurfcsysInfoallocAction()
PURPOSE  : Callback to allocate application data needed to describe
                the surface csys
\*====================================================================*/
ProError UsrSurfcsysInfoallocAction(
    ProAnalysis analysis)
{
    Surfcsysdata_t *data;

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

/*--------------------------------------------------------------------*\
    Put the data pointer into the analysis
\*--------------------------------------------------------------------*/
    ProAnalysisInfoSet(analysis, data);

    return(PRO_TK_NO_ERROR);
}

/*====================================================================*\
FUNCTION : UsrSurfcsysInfofreeAction()
PURPOSE  : Callback to free the application data for surface csys
\*====================================================================*/
ProError UsrSurfcsysInfofreeAction(
    ProAnalysis analysis)
{
    Surfcsysdata_t *data;

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

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

/*--------------------------------------------------------------------*\
    Set the data pointer in the analysis to NULL
\*--------------------------------------------------------------------*/
    ProAnalysisInfoSet(analysis, NULL);

    return(PRO_TK_NO_ERROR);
}

/*====================================================================*\
FUNCTION : UsrSurfcsysCompcheckAction()
PURPOSE  : Callback to tell Pro/E whether computation can be performed
                for surface csys analysis feature
\*====================================================================*/
ProError UsrSurfcsysCompcheckAction(
    ProAnalysis analysis)
{
    Surfcsysdata_t *data;

/*--------------------------------------------------------------------*\
    Get the analysis data
\*--------------------------------------------------------------------*/
    if(ProAnalysisInfoGet(analysis, (ProAppData*)&data) != 
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_GENERAL_ERROR);
}

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

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

/*--------------------------------------------------------------------*\
    Get the location of the datum point
\*--------------------------------------------------------------------*/
    ProSelectionModelitemGet(data->point, &modelitem);
    ProPointInit(modelitem.owner, modelitem.id, &p);
    ProPointCoordGet(p, data->csysdata.origin);

/*--------------------------------------------------------------------*\
    Find the surface the datum point sits on, and find its first
    derivative vectors and the normal
\*--------------------------------------------------------------------*/
    UsrPointSrfGet(data->point, &surface);
    ProSurfaceParamEval(modelitem.owner, surface, data->csysdata.origin, 
uv);
    ProSurfaceXyzdataEval(surface, uv, NULL, der1, NULL, normal);

/*--------------------------------------------------------------------*\
    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
\*--------------------------------------------------------------------*/
    memcpy(data->csysdata.x_vector, der1[0], sizeof(ProVector));
    ProUtilVectorNormalize(data->csysdata.x_vector, 
data->csysdata.x_vector);
    memcpy(data->csysdata.z_vector, normal, sizeof(ProVector));
    ProUtilVectorNormalize(data->csysdata.z_vector, 
data->csysdata.z_vector);
    ProUtilVectorCross(data->csysdata.z_vector,
                        data->csysdata.x_vector, data->csysdata.y_vector);

    return(PRO_TK_NO_ERROR);
}

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

/*--------------------------------------------------------------------*\
    Get the application data from the analysis
\*--------------------------------------------------------------------*/
    if(ProAnalysisInfoGet(analysis, (ProAppData*)&data) != 
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 : UsrSurfcsysOutputAction()
PURPOSE  : Callback to write textual information about the result of
                the computation
\*====================================================================*/
ProError UsrSurfcsysOutputAction(
    ProAnalysis analysis,
    ProLine **lines)
{
    Surfcsysdata_t *data;
    ProCharLine buff;
    ProLine ll[4];

/*--------------------------------------------------------------------*\
    Get the application dara from the analysis
\*--------------------------------------------------------------------*/
    if(ProAnalysisInfoGet(analysis, (ProAppData*)&data) != 
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);

/*--------------------------------------------------------------------*\
    Add the four lines to the output array
\*--------------------------------------------------------------------*/
    ProArrayObjectAdd((ProArray*)lines, -1, 4, ll);

    return(PRO_TK_NO_ERROR);
}

/*====================================================================*\
FUNCTION : UsrSurfcsysSavecheckAction()
PURPOSE  : Callback to tell Pro/E whether the analysis application
                data can be saved
\*====================================================================*/
ProError UsrSurfcsysSavecheckAction(
    ProAnalysis analysis)
{
    return(PRO_TK_NO_ERROR);
}

/*====================================================================*\
FUNCTION : UsrSurfcsysInfosaveAction()
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 UsrSurfcsysInfosaveAction(
    ProAnalysis analysis,
    ProFeature  *feature,
    ProSelection **references)
{
    Surfcsysdata_t *data;
    ProSelection reference;

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

/*--------------------------------------------------------------------*\
    Output the reference to the datum point
\*--------------------------------------------------------------------*/
    ProSelectionCopy(data->point, &reference);
    ProArrayObjectAdd((ProArray*)references, -1, 1, &reference);

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

    return(PRO_TK_NO_ERROR);
}

ProError UsrSurfcsysDimFilter(
    ProDimension *dimension, 
    ProAppData   data)
{
  static ProBoolean first_time = PRO_B_TRUE;
  if (first_time)
  {
    first_time = PRO_B_FALSE;
    return PRO_TK_NO_ERROR;
  }
  else
    return PRO_TK_CONTINUE;
}

ProError UsrSurfcsysDimAction (
    ProDimension  *dimension,
    ProError      status,
    ProAppData     data)
{
  data = (ProAppData)dimension;
  return PRO_TK_NO_ERROR;
}


/*====================================================================*\
FUNCTION : UsrSurfcsysInforetrieveAction()
PURPOSE  : Callback to get the references Pro/E has stored with the
                analysis feature, and retrieve the rest from Ext Data
\*====================================================================*/
ProError UsrSurfcsysInforetrieveAction(
    ProAnalysis analysis,
    ProFeature *feature,
    ProSelection *references)
{
    Surfcsysdata_t *data;
    ProError status;
    ProDimension first_dim;
    double dim_value;

/*--------------------------------------------------------------------*\
    Get the application data from the analysis
\*--------------------------------------------------------------------*/

    if(ProAnalysisInfoGet(analysis, (ProAppData*)&data) != 
PRO_TK_NO_ERROR)
        return(PRO_TK_GENERAL_ERROR);

/*--------------------------------------------------------------------*\
    Copy the first reference to the application as the datum point
\*--------------------------------------------------------------------*/
    ProSelectionCopy(references[0], &data->point);

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

/*--------------------------------------------------------------------*\
    If the feature has a dimension, take its value as the offset
\*--------------------------------------------------------------------*/
    status = ProFeatureDimensionVisit(feature, 
                            (ProDimensionVisitAction)UsrSurfcsysDimAction,
                            (ProDimensionFilterAction)UsrSurfcsysDimFilter,
                            (ProAppData)&first_dim);

    status = ProDimensionValueGet(&first_dim, &dim_value);
    data->offset = dim_value;
    return(PRO_TK_NO_ERROR);
}

/*====================================================================*\
FUNCTION : UsrSurfcsysInfocopyAction()
PURPOSE  : Callback to copy application information from one analysis
                to another.
\*====================================================================*/
ProError UsrSurfcsysInfocopyAction(
    ProAnalysis from,
    ProAnalysis to)
{
    Surfcsysdata_t *fdata, *tdata;

/*--------------------------------------------------------------------*\
    Get the application data from the analyses
\*--------------------------------------------------------------------*/
    if(ProAnalysisInfoGet(from, (ProAppData*)&fdata) != PRO_TK_NO_ERROR)
        return(PRO_TK_GENERAL_ERROR);
    if(ProAnalysisInfoGet(to,   (ProAppData*)&tdata) != 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 : UsrSurfcsysResultAction()
PURPOSE  : Callback to give Pro/E the feature parameters and geometry
                that the analysis feature must contain
\*====================================================================*/
ProError UsrSurfcsysResultAction(
    ProAnalysis analysis,
    ProBoolean names_only,
    ProAnalysisParameter **parameters,
    ProAnalysisGeomitem  **geometry)
{
    Surfcsysdata_t *data;
    ProAnalysisGeomitem geomitem;
    ProAnalysisParameter param;

/*--------------------------------------------------------------------*\
    Get the application data from the analysis
\*--------------------------------------------------------------------*/

    if(ProAnalysisInfoGet(analysis, (ProAppData*)&data) != 
PRO_TK_NO_ERROR)
        return(PRO_TK_GENERAL_ERROR);

/*--------------------------------------------------------------------*\
    Specify a CSYS DATUM with the geometry stored in the application data
\*--------------------------------------------------------------------*/
    ProStringToWstring(geomitem.name,"SURF_CSYS");
    geomitem.create = PRO_B_TRUE;
    geomitem.type = PRO_ANALYSIS_CSYS;
    if(!names_only)
    {
        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
\*--------------------------------------------------------------------*/
    ProArrayAlloc(0, sizeof(ProAnalysisGeomitem), 1, (ProArray*)geometry);
    ProArrayObjectAdd((ProArray*)geometry, -1, 1, &geomitem);

    return(PRO_TK_NO_ERROR);
}

int UsrExtAnalysisRegister ()
{
    ProAnalysisFuncsData data;

    ProStringToWstring(msgfil,"umsg.txt");

/*--------------------------------------------------------------------*\
    Build the description of the surface csys analysis feature
    data, and register it with Pro/E
\*--------------------------------------------------------------------*/
    memset(&data, '\0', sizeof(ProAnalysisFuncsData));
    ProStringToWstring(data.type, "Surface Csys");
    data.ui_action              = UsrSurfcsysUiAction;
    data.dims_action            = UsrSurfcsysDimsAction;
    data.infoalloc_action       = UsrSurfcsysInfoallocAction;
    data.infofree_action        = UsrSurfcsysInfofreeAction;
    data.compcheck_action       = UsrSurfcsysCompcheckAction;
    data.compute_action         = UsrSurfcsysComputeAction;
    data.display_action         = UsrSurfcsysDisplayAction;
    data.output_action          = UsrSurfcsysOutputAction;
    data.savecheck_action       = UsrSurfcsysSavecheckAction;
    data.infosave_action        = UsrSurfcsysInfosaveAction;
    data.inforetrieve_action    = UsrSurfcsysInforetrieveAction;
    data.infocopy_action        = UsrSurfcsysInfocopyAction;
    data.result_action          = UsrSurfcsysResultAction;
    ProAnalysisTypeRegister(&data);

    return(0);
}

/*====================================================================*\
FUNCTION : UsrPointPtypeGet()
PURPOSE  : Utility to get the placement type of a datum point
\*====================================================================*/
int UsrPointPtypeGet(
    ProSolid owner,
    ProPoint p,
    ProDtmpntType *ptype)
{
    ProElempathItem items[5];
    ProElempath epath;
    ProValue value;
    ProValueData vdata;
    int id;
    ProGeomitem geomitem;
    ProFeature feature;

/*--------------------------------------------------------------------*\
    Get the feature that owns the point
\*--------------------------------------------------------------------*/
    ProPointIdGet(p, &id);
    ProModelitemInit(owner, id, PRO_POINT, &geomitem);
    ProGeomitemFeatureGet(&geomitem, &feature);

/*--------------------------------------------------------------------*\
    Get the placement type from the element tree
\*--------------------------------------------------------------------*/
    items[0].type = PRO_ELEM_PATH_ITEM_TYPE_ID;
    items[0].path_item.elem_id = PRO_E_DPOINT_POINTS_ARRAY;

    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_DPOINT_PLA_CONSTRAINTS;

    items[3].type = PRO_ELEM_PATH_ITEM_TYPE_INDEX;
    items[3].path_item.elem_index = 0;

    items[4].type = PRO_ELEM_PATH_ITEM_TYPE_ID;
    items[4].path_item.elem_id = PRO_E_DPOINT_PLA_CONSTR_TYPE;

    ProElempathAlloc(&epath);
    ProElempathDataSet(epath, items, 5);
    if(ProFeatureElemValueGet(&feature, epath, &value) != PRO_TK_NO_ERROR)
        return(0);
    if(ProValueDataGet(value, &vdata) != PRO_TK_NO_ERROR)
        return(0);
    *ptype = (ProDtmpntType)vdata.v.i;
    return(1);
}

/*====================================================================*\
FUNCTION : UsrPointSrfGet()
PURPOSE  : Utility to get the surface a datum point is placed on.
\*====================================================================*/
int UsrPointSrfGet(
    ProSelection point,
    ProSurface *surface)
{
    ProElempathItem items[5];
    ProElempath epath;
    ProValue value;
    ProValueData vdata;
    int id;
    ProGeomitem geomitem;
    ProFeature feature;
    ProSelection sel;

/*--------------------------------------------------------------------*\
    Get the feature that owns the datum point
\*--------------------------------------------------------------------*/
    ProSelectionModelitemGet(point, &geomitem);
    ProGeomitemFeatureGet(&geomitem, &feature);

/*--------------------------------------------------------------------*\
    Get the surface from the element tree
\*--------------------------------------------------------------------*/

    items[0].type = PRO_ELEM_PATH_ITEM_TYPE_ID;
    items[0].path_item.elem_id = PRO_E_DPOINT_POINTS_ARRAY;

    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_DPOINT_PLA_CONSTRAINTS;

    items[3].type = PRO_ELEM_PATH_ITEM_TYPE_INDEX;
    items[3].path_item.elem_index = 0;

    items[4].type = PRO_ELEM_PATH_ITEM_TYPE_ID;
    items[4].path_item.elem_id = PRO_E_DPOINT_PLA_CONSTR_REF;
   
    ProElempathAlloc(&epath);
    ProElempathDataSet(epath, items, 5);
   
    if(ProFeatureElemValueGet(&feature, epath, &value) != PRO_TK_NO_ERROR)
        return(0);

    if(ProValueDataGet(value, &vdata) != PRO_TK_NO_ERROR)
        return(0);

    sel = (ProSelection)vdata.v.r;
    ProSelectionModelitemGet(sel, &geomitem);
    ProSurfaceInit(geomitem.owner, geomitem.id, surface);

    return(1);
}

/*====================================================================*\
FUNCTION : UsrYesnoGet()
PURPOSE  : Prompt for a yes/no answer
\*====================================================================*/
ProError UsrYesnoGet(
    wchar_t *msgfil,
    char *def,      /* INPUT : default answer */
    ProBoolean *yes)/* Output: TRUE = yes, FALSE no */
{
    wchar_t wreply[4];
    char reply[4];
    ProError status;

    while(1)
    {
/*--------------------------------------------------------------------*\
        Read string from keyboard
\*--------------------------------------------------------------------*/
        status = ProMessageStringRead(4, wreply);
        if(status == PRO_TK_MSG_USER_QUIT)
            return(PRO_TK_MSG_USER_QUIT);

/*--------------------------------------------------------------------*\
        Convert reply (or default) to uppercase C string
\*--------------------------------------------------------------------*/
        if(status == PRO_TK_NO_ERROR)
            ProWstringToString(reply,wreply);
        else
            strcpy(reply, def);
        ProUtilStringUpper(reply,reply);

/*--------------------------------------------------------------------*\
        Output response
\*--------------------------------------------------------------------*/
        if(reply[0] == 'Y')
        {
            *yes = PRO_B_TRUE;
            return(PRO_TK_NO_ERROR);
        }
        if(reply[0] == 'N')
        {
            *yes = PRO_B_FALSE;
            return(PRO_TK_NO_ERROR);
        }

/*--------------------------------------------------------------------*\
        If reply was neither yes or no, ask again.
\*--------------------------------------------------------------------*/
       ProMessageDisplay(msgfil,"USER Please answer Y or N : ");
    }
}
 
#define CLASS_NAME "SURFCSYS"

/*====================================================================*\
FUNCTION : UsrOutwardsSave()
PURPOSE  : Save the "outwards" flag to external data
\*====================================================================*/
int UsrOutwardsSave(
    ProFeature *feature,
    ProBoolean outwards)
{
    ProExtdataClass class;
    ProCharName slotname;
    if(!UsrClassRegister(feature->owner, &class))
        return(0);

    ProTKSprintf(slotname,"OUTWARDS_%d",feature->id);
    UsrIntegerSave(&class, slotname, (int)outwards);
    return(1);
}

/*====================================================================*\
FUNCTION : UsrOutwardsRetrieve()
PURPOSE  : Retrieve the outwards flag from external data
\*====================================================================*/
int UsrOutwardsRetrieve(
    ProFeature *feature,
    ProBoolean *outwards)
{
    ProExtdataClass class;
    ProCharName slotname;
    int i;

    if(!UsrClassExists(feature->owner, CLASS_NAME, &class))
        return(0);

    ProTKSprintf(slotname,"OUTWARDS_%d",feature->id);
    if(!UsrIntegerRetrieve(&class, slotname, &i))
        return(0);

    *outwards = i==0 ? PRO_B_FALSE : PRO_B_TRUE;

    return(1);
}

/*====================================================================*\
FUNCTION : UsrClassRegister()
PURPOSE  : Register a data class, if it does not already exist
\*====================================================================*/
int UsrClassRegister(
    ProMdl model,
    ProExtdataClass *class)
{
    ProName cname;

    if(!UsrClassExists(model, CLASS_NAME, class))
    {
        ProStringToWstring(cname, CLASS_NAME);
        if(ProExtdataClassRegister(model, cname, class) != 
PROEXTDATA_TK_NO_ERROR)
            return(0);
    }

    return(1);
}

/*====================================================================*\
FUNCTION : UsrIntegerSave()
PURPOSE  : Utility to save an integer slot
\*====================================================================*/
int UsrIntegerSave(
    ProExtdataClass *class,
    char *slotname,
    int i)
{
    ProExtdataSlot slot;
    ProName sname;

/*--------------------------------------------------------------------*\
    If the specified slot does not exist, create it
\*--------------------------------------------------------------------*/
    ProStringToWstring(sname, slotname);
    if(ProExtdataSlotCreate(class, sname, &slot) != 
PROEXTDATA_TK_NO_ERROR)
    {
        slot.p_class = class;
        ProStringToWstring(slot.slot_name, slotname);
    }

/*--------------------------------------------------------------------*\
    Write the integer to the data slot
\*--------------------------------------------------------------------*/
    ProExtdataSlotWrite(&slot, KEY_BY_NAME, PRO_INTEGER_TYPE, 0, &i);

    return(1);
}

/*====================================================================*\
FUNCTION : UsrIntegerRetrieve()
PURPOSE  : Retrieve an integer
\*====================================================================*/
int UsrIntegerRetrieve(
    ProExtdataClass *class,
    char *slotname,
    int *i)
{
    ProExtdataSlot slot;
    int *data;
    int size, type;

/*--------------------------------------------------------------------*\
    Read the integer from the data slot
\*--------------------------------------------------------------------*/
    slot.p_class = class;
    ProStringToWstring(slot.slot_name, slotname);
    if(ProExtdataSlotRead(&slot, KEY_BY_NAME, &type, &size, (void**)&data)
                != PROEXTDATA_TK_NO_ERROR)
        return(0);

    *i = *data;

    return(1);
}

/*====================================================================*\
    FUNCTION :  UsrClassExists
    PURPOSE  :  Check if the specified class exists
\*====================================================================*/
int UsrClassExists(
    ProMdl model,
    char *name,
    ProExtdataClass *class)
{
    ProExtdataErr status;
    int n_classes, c;
    ProName *classes;

    status = ProExtdataClassNamesList(model, &n_classes, &classes);
    if(status != PROEXTDATA_TK_NO_ERROR)
        return(0);

    for(c=0;c<n_classes;c++)
    {
        if(!ProUtilStrwscmp(name, classes[c]))
        {
            class->p_model = model;
            ProStringToWstring(class->class_name, name);
            ProArrayFree((ProArray *)&classes);
            return(1);
        }
    }

    ProArrayFree((ProArray *)&classes);

    return(0);
}