/*
	Copyright (c) 2019 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 <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; 
    ProSelection ref; 
    ProGtol gtol;

/*--------------------------------------------------------------------*\ 
    Allocate the gtol data structure 
\*--------------------------------------------------------------------*/ 
    ProGtoldataAlloc(reference->owner, &gdata);

/*--------------------------------------------------------------------*\ 
    Set the gtol type 
\*--------------------------------------------------------------------*/ 
    ProGtoldataTypeSet(gdata, PROGTOLTYPE_POSITION, &gstatus);

/*--------------------------------------------------------------------*\ 
    Set the gtol model 
    \*--------------------------------------------------------------------*/ 
    ProGtoldataModelSet(gdata, reference->owner, &gstatus);

/*--------------------------------------------------------------------*\ 
    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);

/*--------------------------------------------------------------------*\ 
    Set the placement using the leader and the specified position 
\*--------------------------------------------------------------------*/ 
    ProGtoldataPlacementSet(gdata, PROGTOLPTYPE_LEADERS, NULL, leaders,
                        pos, NULL, &gstatus);

/*--------------------------------------------------------------------*\ 
    Set the annotation plane 
\*--------------------------------------------------------------------*/ 
    ProGtoldataPlaneSet(gdata, ap);

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

/*--------------------------------------------------------------------*\ 
    Set up a ProSelection for the datum, and set it as the basic reference 
\*--------------------------------------------------------------------*/ 
    ProSelectionAlloc(NULL, reference, &ref); 
    ProGtoldatumrefAlloc(ref, PROGTOLMATCOND_DEFAULT_RFS, NULL,
                    PROGTOLMATCOND_DEFAULT_RFS, &datumref);  
    ProGtoldataGtoldatumrefSet(gdata, datumref, NULL, NULL, &gstatus);
    ProGtoldatumrefFree(&datumref);

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

/*--------------------------------------------------------------------*\ 
    Create the tolerance 
\*--------------------------------------------------------------------*/ 
    status = ProGtolCreate(gdata, &gtol);

/*--------------------------------------------------------------------*\ 
    Free the gtol data 
\*--------------------------------------------------------------------*/ 
    ProGtoldataFree(&gdata);
    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(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;

    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 surfaces, attaching a gtol to each one which is
    plane and parallel to the reference. 
\*--------------------------------------------------------------------*/
    ProSolidSurfaceVisit(datum.owner, UsrSurfAction, NULL, &data);

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

    return(1); 
}