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


/*--------------------------------------------------------------------*\
Pro/Toolkit includes
\*--------------------------------------------------------------------*/
#include <ProToolkit.h>
#include <ProSelection.h>
#include <ProObjects.h>
#include <ProSolid.h>
#include <ProGeomitem.h>
#include <ProModelitem.h>
#include <ProMdl.h>
#include <ProExtobj.h>
#include <ProExtobjDisp.h>
#include <ProMessage.h>
#include <ProSurface.h>

/*--------------------------------------------------------------------*\
Application includes
\*--------------------------------------------------------------------*/
#include "ProUtil.h"
#include "TestError.h"
#include "UtilMath.h"
#include "math.h"

/*--------------------------------------------------------------------*\
Application data
\*--------------------------------------------------------------------*/
static ProExtobjClass User_arrow_class ; 

/*====================================================================*\
FUNCTION : UserExtobjCreate()
PURPOSE  : Example of external object creation.
\*====================================================================*/
int UserExtobjCreate() 
{
    static int calls = 0 ; 		/* number of calls to this func. */
    static int extobjid = 1 ;           /* ids for external objects */
    
    int UserMakeSelections(ProSurface *, ProModelitem *, Pro3dPnt ) ; 
    					/* function to get user selections */
					
    ProMdl model ; 			/* current model */
    ProName msg_file ;                  /* message file */
    ProSurface surf ;  			/* target surface to get arrow */
    ProModelitem surf_mdlitem ;   	/* target surface ProModelitem */
    ProModelitem surf_owner_mdlitem ;  	/* ProModelitem for surface owner */
    int status ; 			/* return value */
    ProUvParam surf_uv ;  	 	/* surface point in uv*/
    ProVector surf_xyz ; 		/* surface point in xyz */
    ProVector deriv1[2], deriv2[3] ;   	/* derivatives at point on surf */
    ProVector surf_norm; 		/* surface normal */
    ProExtobj arrow_obj ; 		/* ext. object to be created */
    ProWExtobjdata arrow_disp_data = NULL ;  
                                        /* display data for the ext. object */
    double scale ;        		/* scale factor for ext. object */
    Pro3dPnt outline_points[2] ; 	/* points delimiting solid */
    ProCurvedata curvedata[10] ; 	/* array of curves to be drawn */
    int i, j ; 				/* counters */
    ProMatrix transform ; 		/* arrow transformation */
    int num_box;    /* number of selection boxes */
    ProWExtobjdata	sel_data = NULL; /* selection data */
    ProSelbox  sel_box[16], *sel_box_ptr; /* list of selection boxes */

/*--------------------------------------------------------------------*\
    Unit arrow to be transformed and scaled.
\*--------------------------------------------------------------------*/
    #define ARROW_COLOR PRO_COLOR_SHEETMETAL       /* Green. */
    #define ARROW_LINESTYLE PRO_LINESTYLE_SOLID    /* Solid lines. */
    #define ARROW_SCALE 0.1 	/* Scale factor (fraction of model size). */
    #define ARROW_NUM_SEG 9 	/* Number of segments in arrow. */
    #define HW 0.2              /* Head width. */
    #define HL 0.2  		/* Head length. */
    ProLinedata unit_arrow[ARROW_NUM_SEG] = {
        { PRO_ENT_LINE, {HL,0.0,0.0}, {1.0,0.0,0.0} },  /* arrow "shaft" */
        { PRO_ENT_LINE, {0.0,0.0,0.0}, {HL,HW,0.0} }, 
        { PRO_ENT_LINE, {0.0,0.0,0.0}, {HL,0.0,HW} }, 
        { PRO_ENT_LINE, {0.0,0.0,0.0}, {HL,-HW,0.0} }, 
        { PRO_ENT_LINE, {0.0,0.0,0.0}, {HL,0.0,-HW} }, 
        { PRO_ENT_LINE, {HL,HW,0.0}, {HL,0.0,HW} }, 
        { PRO_ENT_LINE, {HL,0.0,HW}, {HL,-HW,0.0} }, 
        { PRO_ENT_LINE, {HL,-HW,0.0}, {HL,0.0,-HW} }, 
        { PRO_ENT_LINE, {HL,0.0,-HW}, {HL,HW,0.0} } 
	} ; 

/*--------------------------------------------------------------------*\
    Get current model.
\*--------------------------------------------------------------------*/
    status = ProMdlCurrentGet(&model);
    ERROR_CHECK("UserExtobjCreate()", "ProMdlCurrentGet()", status); 
    if (status != PRO_TK_NO_ERROR)
    {
       ProStringToWstring(msg_file, "msg_ugfund.txt") ; 
       ProMessageDisplay(msg_file, "USER %0s F", "Error getting current model."); 
       return(status) ; 
    }
       
/*--------------------------------------------------------------------*\
    Create the external object class. 
\*--------------------------------------------------------------------*/
    if (calls == 0) 
    {
	ProStringToWstring(User_arrow_class.name, "Arrows");
	User_arrow_class.type = 0;

        status = ProExtobjClassCreate(&User_arrow_class) ; 
	ERROR_CHECK("UserExtobjCreate()", "ProExtobjClassCreate()", status); 
    }
    ++calls ; 

/*--------------------------------------------------------------------*\
    Use model size to get the scale.  
\*--------------------------------------------------------------------*/
    status = ProSolidOutlineGet( (ProSolid) model, outline_points) ; 
    ERROR_CHECK("UserExtobjCreate()", "ProSolidOutlineGet()", status); 
    scale = ARROW_SCALE * ProUtilPointsDist (outline_points[1], 
        outline_points[0]);

/*--------------------------------------------------------------------*\
    Get ProSurface and ProModelitem from user selection.   Also get the 
    xyz point that is selected.
\*--------------------------------------------------------------------*/
    status = UserMakeSelections(&surf, &surf_mdlitem, surf_xyz) ; 
    ERROR_CHECK("UserExtobjCreate()", "UserMakeSelections()", status); 

/*--------------------------------------------------------------------*\
    Get the ProModelitem for the surface owner.
\*--------------------------------------------------------------------*/
    status = ProMdlToModelitem(surf_mdlitem.owner, &surf_owner_mdlitem) ; 

/*--------------------------------------------------------------------*\
    Determine the uv parameters from the xyz point.
\*--------------------------------------------------------------------*/
    status = ProSurfaceParamEval( (ProSolid) surf_mdlitem.owner, surf, 
        surf_xyz, surf_uv) ; 
    ERROR_CHECK("UserExtobjCreate()", "ProSurfaceParamEval()", status); 

/*--------------------------------------------------------------------*\
    Evaluate surface data (normal, gradient, ...) at the uv point.
\*--------------------------------------------------------------------*/
    status = ProSurfaceXyzdataEval(surf, surf_uv, surf_xyz, 
        deriv1, deriv2, surf_norm); 
    ERROR_CHECK("UserExtobjCreate()", "ProSurfaceXyzdataEval()", status); 

/*--------------------------------------------------------------------*\
    Get transformation for the arrow.
\*--------------------------------------------------------------------*/
    ProUtilVectorNormalize(surf_norm, surf_norm) ; 
    ProUtilVectorNormalize(deriv1[0], deriv1[0]) ; 
    ProUtilVectorNormalize(deriv1[1], deriv1[1]) ; 
    status =  ProUtilVectorsToTransf(surf_norm, deriv1[0], deriv1[1], 
        surf_xyz, transform) ; 

/*--------------------------------------------------------------------*\
    Create the arrow object.
\*--------------------------------------------------------------------*/
    status = ProExtobjCreate(&User_arrow_class, &surf_owner_mdlitem, 
        &arrow_obj) ; 
    ERROR_CHECK("UserExtobjCreate()", "ProExtobjCreate()", status); 

/*--------------------------------------------------------------------*\
    Set object identifiers to be reusable.
\*--------------------------------------------------------------------*/
    status = ProExtobjReusableSet(&arrow_obj) ; 
    ERROR_CHECK("UserExtobjCreate()", "ProExtobjReusableSet()", status); 

/*--------------------------------------------------------------------*\
    Initialize the display data structure.
\*--------------------------------------------------------------------*/
    status = ProDispdatAlloc(&arrow_disp_data) ; 
    ERROR_CHECK("UserExtobjCreate()", "ProDispdatAlloc()", status); 

/*--------------------------------------------------------------------*\
    Set the display scale, color, linetype, and display properties. 
\*--------------------------------------------------------------------*/
    status = ProDispdatScaleSet(arrow_disp_data, scale) ; 
    ERROR_CHECK("UserExtobjCreate()", "ProDispdatScaleSet()", status); 
		
    status = ProDispdatColorSet(arrow_disp_data, ARROW_COLOR) ; 
    ERROR_CHECK("UserExtobjCreate()", "ProDispdatColorSet()", status); 
		
    status = ProDispdatLinestyleSet(arrow_disp_data, ARROW_LINESTYLE) ; 
    ERROR_CHECK("UserExtobjCreate()", "ProDispdatLinestyleSet()", status); 

/*--------------------------------------------------------------------*\
    Set the line segments (the "entities") in the display data.
\*--------------------------------------------------------------------*/
    for (i = 0; i < ARROW_NUM_SEG; ++i) 
    {
        curvedata[i].line.type = unit_arrow[i].type ;  
	for (j = 0; j<3 ; ++j)
	{
	    curvedata[i].line.end1[j] = unit_arrow[i].end1[j] ; 
	    curvedata[i].line.end2[j] = unit_arrow[i].end2[j] ; 
	}
    }

    status = ProDispdatEntsSet(arrow_disp_data, &curvedata[0], ARROW_NUM_SEG) ; 
    ERROR_CHECK("UserExtobjCreate()", "ProDispdatEntsSet()", status); 

/*--------------------------------------------------------------------*\
    Set arrow transformation.
\*--------------------------------------------------------------------*/
    status = ProDispdatTrfSet(arrow_disp_data, transform) ; 
    ERROR_CHECK("UserExtobjCreate()", "ProDispdatTrfSet()", status); 

/*--------------------------------------------------------------------*\
    Add the display data to the object.  
\*--------------------------------------------------------------------*/
    status = ProExtobjdataAdd(&arrow_obj, &User_arrow_class, 
        arrow_disp_data) ; 
    ERROR_CHECK("UserExtobjCreate()", "ProExtobjdataAdd()", status); 

    status = ProSeldatAlloc (&sel_data);
    ERROR_CHECK("UserExtobjCreate()", "ProSeldatAlloc()", status); 

    sel_box_ptr = &sel_box[0];
    (*sel_box_ptr)[0][0] = 0.0;
    (*sel_box_ptr)[0][1] = 0.0;
    (*sel_box_ptr)[0][2] = 0.0;
    (*sel_box_ptr)[1][0] = 0.5;
    (*sel_box_ptr)[1][1] = 0.5;
    (*sel_box_ptr)[1][2] = 0.5;
    num_box = 1;

    status = ProSeldatSelboxesSet (sel_data, sel_box, num_box);
    ERROR_CHECK("UserExtobjCreate()", "ProSeldatSelboxesSet()", status); 

    status = ProExtobjdataAdd (&arrow_obj, &User_arrow_class, sel_data);
    ERROR_CHECK("UserExtobjCreate()", "ProExtobjdataAdd()", status); 

    status = ProExtobjdataFree (&sel_data);
    ERROR_CHECK("UserExtobjCreate()", "ProExtobjdataFree()", status); 

/*--------------------------------------------------------------------*\
    Repaint the window so the object is displayed.
\*--------------------------------------------------------------------*/
    ProWindowRepaint(PRO_VALUE_UNUSED) ; 

    return(PRO_TK_NO_ERROR) ; 
}