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

#include <ProToolkit.h>
#include <ProSurface.h>
#include <ProArray.h>
#include <ProGtol.h>
#include <ProUtil.h>
#include <ProModelitem.h>
#include <ProMessage.h>
#include <ProAnnotation.h>
#include <ProSolid.h>
#include <ProDisplist.h>
#include <ProWindows.h>
#include <ProGtolAttach.h>

#include <UtilMessage.h>
#include <PTApplsUnicodeUtils.h>
#define EPSM6 0.0001
/*---------------------------------------------------------------------*\
     Data structure for finding parallel solid plane surfaces 
\*---------------------------------------------------------------------*/
 typedef struct planes_data 
{
    ProGeomitem reference; 
    ProAnnotationPlane ap;
    ProVector normal; 
    double tolerance; 
} Planesdata_t;

/*====================================================================*\ 
FUNCTION: UsrPlanePositiontolSet() 
PURPOSE:  To add a position gtol to the specified surface 
\*====================================================================*/ 
int UsrPlanePositiontolSet( 
    ProSelection surface, /* The surface */ 
    ProVector pos, /* The position of the gtol */ 
    ProGeomitem *reference, /* The datum reference */ 
    ProAnnotationPlane* ap, /* The annotation plane */
    double tolerance) /* The tolerance value */ 
{ 
    ProError status; 
    ProGtoldata gdata; 
    ProGtoldataStatus gstatus; 
    ProGtolleader leader, *leaders; 
    ProName wname; 
    ProCharName name; 
    ProModelitem modelitem; 
    ProGtoldatumref datumref; 
    ProGtol gtol;
    ProGtolAttach gtolAttach;
    ProPath path;
    ProAnnotationReference* refs = NULL;
    wchar_t* symStr = NULL;

/*--------------------------------------------------------------------*\ 
    Set the reference to the surface 
\*--------------------------------------------------------------------*/ 
  //  ProGtoldataReferenceSet(gdata, PROGTOLRTYPE_SURF, surface, &gstatus);

/*--------------------------------------------------------------------*\ 
    Allocate a leader which is attached to the surface 
\*--------------------------------------------------------------------*/ 
    ProGtolleaderAlloc(PROLEADERTYPE_ARROWHEAD, surface, &leader);

/*--------------------------------------------------------------------*\ 
    Set up an array of leaders with the one leader in it 
\*--------------------------------------------------------------------*/ 
    ProArrayAlloc(0, sizeof(ProGtolleader), 1, (ProArray)&leaders); 
    ProArrayObjectAdd((ProArray)&leaders, -1, 1, &leader);

/*--------------------------------------------------------------------*\ 
    Free the leader 
\*--------------------------------------------------------------------*/ 
    ProGtolleaderFree(&leader);

/*--------------------------------------------------------------------*\ 
    Set up a ProSelection for the datum, and set it as the basic reference 
\*--------------------------------------------------------------------*/ 

    status = ProArrayAlloc(1, sizeof(ProAnnotationReference), 1, (ProArray*)&refs);

    refs[0].type = PRO_ANNOT_REF_SINGLE;
    refs[0].object.reference = NULL;

    status = ProSelectionToReference(surface, &(refs[0].object.reference));
    status = ProGtolSymbolStringGet(PRO_GTOL_SYMBOL_DEFAULT_RFS, PRO_FONT_ISO, &symStr);

/*--------------------------------------------------------------------*\ 
    Set the tolerance value 
\*--------------------------------------------------------------------*/ 
    ProSelectionModelitemGet(surface, &modelitem); 
    ProTKSprintf(name, "surf%d",modelitem.id); ProStringToWstring(wname, name);
   // ProGtoldataValueSet(gdata, PRO_B_TRUE, tolerance, wname, &gstatus);

    status = ProGtolAttachAlloc(reference->owner, &gtolAttach);

    status = ProGtolAttachLeadersSet(gtolAttach, ap, PRO_GTOL_LEADER, leaders, pos);

/*--------------------------------------------------------------------*\ 
    Create the tolerance 
\*--------------------------------------------------------------------*/ 
    ProStringToWstring(path, "gp");

    ProWstringConcatenate(symStr, path, PRO_VALUE_UNUSED);

    status = ProMdlGtolCreate(reference->owner, PROGTOLTYPE_POSITION, gtolAttach, path, &gtol);

    status = ProGtolReferencesAdd(&gtol, refs);

    status = ProArrayFree((ProArray*)&refs);

/*--------------------------------------------------------------------*\ 
    Free the gtol data 
\*--------------------------------------------------------------------*/ 
    return(status == PRO_TK_NO_ERROR ? 1 : 0); 
}

/*====================================================================*\ 
FUNCTION: UsrSurfAction() 
PURPOSE:  Action function called when visiting solid surfaces to 
               attach gtol to. 
\*====================================================================*/ 
ProError UsrSurfAction( 
    ProSurface surface, 
    ProError filt_status, 
    ProAppData data) 
{ 
    Planesdata_t *pdata=(Planesdata_t*)data; 
    ProVector normal, cross, pos; 
    ProUvParam uv; 
    ProSrftype stype; 
    int id; 
    ProModelitem modelitem; 
    ProSelection sel;

/*--------------------------------------------------------------------*\ 
    If the surface is not a plane, skip it. 
\*--------------------------------------------------------------------*/ 
    ProSurfaceTypeGet(surface, &stype); 
    if(stype != PRO_SRF_PLANE) 
        return(PRO_TK_NO_ERROR);

/*--------------------------------------------------------------------*\ 
    If the surface is not parallel to the reference datum, skip it. 
\*--------------------------------------------------------------------*/ 
    uv[0]=uv[1]=0.0; 
    ProSurfaceXyzdataEval(surface, uv, pos, NULL, NULL, normal); 
    ProUtilVectorCross(normal, pdata->normal, cross);
    if(fabs((double)ProUtilVectorLength(cross)) > EPSM6) 
        return(PRO_TK_NO_ERROR);

/*--------------------------------------------------------------------*\ 
    Set the position of the gtol to be the point for zero UV, offset 
    by the outward normal. 
\*--------------------------------------------------------------------*/ 
    pos[0] += normal[0]; 
    pos[1] += normal[1]; 
    pos[2] += normal[2];

/*--------------------------------------------------------------------*\ 
    Add the gtol to the surface 
\*--------------------------------------------------------------------*/ 
    ProSurfaceIdGet(surface, &id); 
    ProModelitemInit(pdata->reference.owner, id, PRO_SURFACE, &modelitem);
    ProSelectionAlloc(NULL, &modelitem, &sel); 
    UsrPlanePositiontolSet(sel, pos, &pdata->reference, &pdata->ap,  pdata->tolerance);

    return(PRO_TK_NO_ERROR); 
}

/*====================================================================*\ 
FUNCTION: UsrPlanesTol() 
PURPOSE:  Command to add a position gtol to all solid planes that are
          parallel to a selected datum. Makes the selected datum 
          into a gtol reference if required. 
\*====================================================================*/ 
int UsrPlanesTol() 
{ 
    ProError status; 
    ProSelection *sel; 
    int n_sel; 
    ProGeomitem datum; 
    ProName wname; 
    ProBoolean ref_datum, is_in_dim; 
    ProDimension dim; 
    Planesdata_t data; 
    ProUvParam uv; 
    ProSurface surface;
    ProFileName msgfil;
    ProAnnotationPlane ap;
    ProVector normal;
    ProModelitem ap_datum;
    ProSurface ap_surf;
    ProGeomitemdata* gdata;
    ProSolidBody* bodies = NULL;
    int i = 0, body_num = 0;

    ProStringToWstring (msgfil, "msg_uggtol.txt");

/*--------------------------------------------------------------------*\ 
    Select the datum 
\*--------------------------------------------------------------------*/ 
    ProMessageDisplay(msgfil,
		      "USER Select a datum plane for gtol references"); 

    if(ProSelect("datum",1,NULL,NULL,NULL,NULL,&sel,&n_sel) 
                != PRO_TK_NO_ERROR || n_sel < 0) 
        return(0); 

    ProSelectionModelitemGet(sel[0], &datum);

/*--------------------------------------------------------------------*\ 
    Convert it's type to be a DATUM_PLANE 
\*--------------------------------------------------------------------*/ 
    ProModelitemInit(datum.owner, datum.id, PRO_DATUM_PLANE, &datum);
    ProModelitemNameGet(&datum, wname);

/*--------------------------------------------------------------------*\ 
    Is the datum a gtol reference? 
\*--------------------------------------------------------------------*/ 
    ProGeomitemIsGtolref(&datum, &ref_datum, &is_in_dim, &dim);

/*--------------------------------------------------------------------*\ 
    If so, say so; if not, ask whether it should be made one 
\*--------------------------------------------------------------------*/ 
    if(ref_datum) 
        ProMessageDisplay(msgfil,"USER %0w is already a reference datum",
                   wname); 

    else 
    { 
        ProMessageDisplay(msgfil,"USER %0w is not a reference datum." 
                   "Do you wish to set it (yes/no)?|||yes", wname);
	ref_datum = ProUtilYesnoGet("YES");

/*--------------------------------------------------------------------*\ 
        If "no" then exit, else set the datum ad a gtol reference 
\*--------------------------------------------------------------------*/ 
        if(!ref_datum) 
            return(0);

        ProGeomitemSetdatumSet (&datum, NULL);
    }

/*--------------------------------------------------------------------*\ 
    Remember the reference 
\*--------------------------------------------------------------------*/ 
    memcpy(&data.reference, &datum, sizeof(ProGeomitem));

/*--------------------------------------------------------------------*\ 
    Calculate the normal, used in checking for parallel surfaces 
\*--------------------------------------------------------------------*/ 
    ProSurfaceInit(datum.owner, datum.id, &surface); 
    uv[0]=uv[1]=0.0;
    ProSurfaceXyzdataEval(surface, uv, NULL, NULL, NULL, data.normal);

/*--------------------------------------------------------------------*\ 
    Ask the user for the annotation plane
\*--------------------------------------------------------------------*/ 
    ProMessageDisplay(msgfil,"USER Select the annotation plane to use");
    if (ProSelect ("datum", 1, NULL, NULL, NULL, NULL, &sel, &n_sel))
      return (0);
    
/*--------------------------------------------------------------------*\ 
    Create the annotation plane using the default normal vector 
\*--------------------------------------------------------------------*/ 
    status = ProSelectionModelitemGet (sel[0], &ap_datum);	
    status = ProGeomitemToSurface (&ap_datum, &ap_surf);
    status = ProSurfaceDataGet (ap_surf, &gdata); 
    memcpy (normal, gdata->data.p_surface_data->srf_shape.plane.e3, sizeof (ProVector));
    
    ProGeomitemdataFree (&gdata);
    
    status = ProAnnotationplaneCreate (sel[0], normal, &ap);
    memcpy(&data.ap, &ap, sizeof(ProAnnotationPlane));
    
/*--------------------------------------------------------------------*\ 
    Ask the user for the tolerance value 
\*--------------------------------------------------------------------*/ 
    ProMessageDisplay(msgfil,"USER Enter the tolerance value|||0.1");
    status = ProMessageDoubleRead(NULL, &data.tolerance); 
    if(status == PRO_TK_MSG_USER_QUIT) 
        return(0); 
    if(status != PRO_TK_NO_ERROR) 
        data.tolerance = 0.1;

/*--------------------------------------------------------------------*\ 
    Visit all the solid bodies surfaces, attaching a gtol to each one which is
    plane and parallel to the reference. 
\*--------------------------------------------------------------------*/
    status = ProSolidBodiesCollect(datum.owner, &bodies);
						
	if (status == PRO_TK_NO_ERROR)
    {
        status = ProArraySizeGet(bodies, &body_num);
		 
	    for (i=0 ; i<body_num ; i++)
	      status = ProSolidBodySurfaceVisit((ProSolidBody*)&bodies[i], UsrSurfAction, &data);
	}				

/*--------------------------------------------------------------------*\ 
    Repaint the model to show the added geometric tolerances
\*--------------------------------------------------------------------*/
    ProDisplistInvalidate (datum.owner);
    ProWindowRepaint (PRO_VALUE_UNUSED);

    return(1); 
}