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

#include <ProToolkit.h>
#include <ProDwgtable.h>
#include <ProPoint.h>

/*--------------------------------------------------------------------*\ 
    Data structure for information needed about datum points 
\*--------------------------------------------------------------------*/ 
typedef struct point_t {
     ProFeature feature;
     ProVector  position;
     ProName    name;
 } Point_t;

int UsrPointsCollect();
int UsrTableTextAdd();
/*====================================================================*\ 
FUNCTION : UsrPointTable() 
PURPOSE  : Command to create a table of datum points 
\*====================================================================*/ 
int UsrPointTable ()
{
     ProError status;
     ProSelection *sel;
     int n_sel, n_points, p;
     ProSolid solid;
     ProCharName csys_name;
	 char name[PRO_MDLNAME_SIZE], type[PRO_MDLEXTENSION_SIZE];
     ProName wname;
     ProGeomitem csys_geom;
     ProAsmcomppath csys_comppath;
     ProMatrix from_csys, to_csys, trf;
     ProGeomitemdata *gdata;
     ProMouseButton button;
     ProVector pos;
     Point_t *points;
     ProDwgtabledata tdata;
     double widths[] = {8.0, 8.0, 10.0, 10.0, 10.0};
     ProHorzJust justs[] = {
                            PROHORZJUST_LEFT,
                            PROHORZJUST_LEFT,
                            PROHORZJUST_LEFT,
                            PROHORZJUST_LEFT,
                            PROHORZJUST_LEFT};
     double *heights;
     int last_feat_row;
     ProDrawing drawing;
     ProDwgtable table;     
     ProCharLine line;
     ProFileName msgfil;
	 ProMdlName modelName;
	 ProMdlExtension modelExtension;

     ProStringToWstring (msgfil, "msg_ugdrawing.txt");

/*--------------------------------------------------------------------*\ 
    Select a coordinate system. This defines the model (the top one
     in that view), and the reference for the datum point positions. 
\*--------------------------------------------------------------------*/ 
     ProMessageDisplay(msgfil,"USER Select csys");
     status = ProSelect("csys",1,NULL,NULL,NULL,NULL,&sel,&n_sel);
     if(status != PRO_TK_NO_ERROR || n_sel < 1)
         return(0);

/*--------------------------------------------------------------------*\ 
    Extract the csys handle, and assembly path. 
\*--------------------------------------------------------------------*/ 
     ProSelectionModelitemGet(sel[0], &csys_geom);
     ProSelectionAsmcomppathGet(sel[0], &csys_comppath);

/*--------------------------------------------------------------------*\ 
    Extract the csys location 
\*--------------------------------------------------------------------*/ 
     ProGeomitemdataGet(&csys_geom, &gdata);
     ProMatrixInit(gdata->data.p_csys_data->x_vector,
                   gdata->data.p_csys_data->y_vector,
                   gdata->data.p_csys_data->z_vector,
                   gdata->data.p_csys_data->origin,
                         from_csys);

     ProUtilMatrixInvert(from_csys, to_csys);
     ProGeomitemdataFree(&gdata);

/*--------------------------------------------------------------------*\ 
    Extract the csys name 
\*--------------------------------------------------------------------*/ 
     ProModelitemNameGet(&csys_geom, wname);
     ProWstringToString(csys_name, wname);

/*--------------------------------------------------------------------*\ 
    Get the root solid, and the transform from the root to the
     component owning the csys 
\*--------------------------------------------------------------------*/ 
    
     if(csys_comppath.table_num > 0)
     {         
	solid = csys_comppath.owner;
        ProAsmcomppathTrfGet(&csys_comppath, PRO_B_FALSE, trf);
     }     	
     else
     {         
	solid = csys_geom.owner;
        ProUtilMatrixCopy(NULL, trf);
     }

/*--------------------------------------------------------------------*\ 
    Get a list of datum points in the model 
\*--------------------------------------------------------------------*/ 
     UsrPointsCollect(solid, &points);
     ProArraySizeGet(points, &n_points);
     if(n_points < 1)
         return(0);

/*--------------------------------------------------------------------*\ 
    Get the user to select the table position 
\*--------------------------------------------------------------------*/ 
     ProMessageDisplay(msgfil,"USER Pick table position");
     if(ProMousePickGet(PRO_ANY_BUTTON, &button, pos) != PRO_TK_NO_ERROR)
         return(0);

/*--------------------------------------------------------------------*\ 
    Setup the table data 
\*--------------------------------------------------------------------*/ 
     ProDwgtabledataAlloc(&tdata);
     ProDwgtabledataOriginSet(tdata, pos);
     ProDwgtabledataSizetypeSet(tdata, PRODWGTABLESIZE_CHARACTERS);
     ProDwgtabledataColumnsSet(tdata, 5, widths, justs);

     heights = (double*)calloc(n_points+2, sizeof(double));
     for(p=0;p<n_points+2;p++)
         heights[p] = 1.0;
     ProDwgtabledataRowsSet(tdata, n_points+2, heights);
     free(heights);

/*--------------------------------------------------------------------*\ 
    Create the table 
\*--------------------------------------------------------------------*/ 
     ProMdlCurrentGet((ProMdl*)&drawing);
     ProDrawingTableCreate(drawing, tdata, PRO_B_FALSE, &table);

/*--------------------------------------------------------------------*\ 
    Merge the top row cells to form the header 
\*--------------------------------------------------------------------*/ 
     ProDwgtableCellsMerge(&table, 1, 1, 5, 1, PRO_B_FALSE);

/*--------------------------------------------------------------------*\ 
    Write header text specifying model and csys 
\*--------------------------------------------------------------------*/ 
     
	 ProMdlMdlnameGet(solid, modelName);        
	
     ProMdlExtensionGet(solid, modelExtension);
     

     ProWstringToString(name, modelName);
     ProWstringToString(type, modelExtension);

     ProTKSprintf(line, "Datum points for %s.%s, w.r.t csys %s\n",
                 name, type, csys_name);

     UsrTableTextAdd(&table, 1, 1, line);

/*--------------------------------------------------------------------*\ 
    Add subheadings to columns 
\*--------------------------------------------------------------------*/ 
     UsrTableTextAdd(&table, 1, 2, "Feat id");
     UsrTableTextAdd(&table, 2, 2, "Point");
     UsrTableTextAdd(&table, 3, 2, "X");
     UsrTableTextAdd(&table, 4, 2, "Y");
     UsrTableTextAdd(&table, 5, 2, "Z");

/*--------------------------------------------------------------------*\ 
    For each datum point... 
\*--------------------------------------------------------------------*/ 
    for(p=0;p<n_points;p++)
     { 
/*--------------------------------------------------------------------*\ 
        If the owning feature is the same as the last one, just
         merge column 1 with the cell above, else enter the feature id 
\*--------------------------------------------------------------------*/ 
        if(p == 0 || points[p].feature.id != points[p-1].feature.id)
         {
             ProTKSprintf(name, "%d", points[p].feature.id);
             UsrTableTextAdd(&table, 1, p+3, name);
             last_feat_row = p+3;
         }

         else
             ProDwgtableCellsMerge(&table, 1, last_feat_row, 1, p+3,
                                    PRO_B_FALSE);

/*--------------------------------------------------------------------*\ 
        Add the point name to column 2 
\*--------------------------------------------------------------------*/ 
         ProWstringToString(name, points[p].name);
         UsrTableTextAdd(&table, 2, p+3, name);

/*--------------------------------------------------------------------*\ 
        Transform the location w.r.t to the csys 
\*--------------------------------------------------------------------*/ 
         ProPntTrfEval(points[p].position, trf,     points[p].position);
         ProPntTrfEval(points[p].position, to_csys, points[p].position);

/*--------------------------------------------------------------------*\ 
        Add the XYZ to column 3,4,5 
\*--------------------------------------------------------------------*/ 
         ProTKSprintf(name,"%8.3f", points[p].position[0]);
         UsrTableTextAdd(&table, 3, p+3, name);
         ProTKSprintf(name,"%8.3f", points[p].position[1]);
         UsrTableTextAdd(&table, 4, p+3, name);
         ProTKSprintf(name,"%8.3f", points[p].position[2]);
         UsrTableTextAdd(&table, 5, p+3, name);     
}

/*--------------------------------------------------------------------*\ 
    Display the table 
\*--------------------------------------------------------------------*/ 
    ProDwgtableDisplay(&table);

    return(1);
 }

/*====================================================================*\ 
FUNCTION : UsrTableTextAdd() 
PURPOSE  : Utility to add one text line to a table cell 
\*====================================================================*/ 
int UsrTableTextAdd(
     ProDwgtable *table,
     int col,
     int row,
     char *text)
 {
     ProWstring *lines;
     ProArrayAlloc(1, sizeof(ProWstring), 1, (ProArray*)&lines);
     lines[0] = (wchar_t*)calloc(strlen(text) + 1, sizeof(wchar_t));
     ProStringToWstring(lines[0], text);
     ProDwgtableTextEnter(table, col, row, lines);
     ProArrayFree((ProArray*)&lines);
 }

/*====================================================================*\ 
FUNCTION : UsrPointAction() 
PURPOSE  : Visit action function called for each datum point 
\*====================================================================*/ 
ProError UsrPointAction(
     ProGeomitem *geomitem,
     ProError filt_status,
     ProAppData data)
 {
     Point_t point;
     ProPoint p;
/*--------------------------------------------------------------------*\ 
    Find out which feature the datum point belongs to 
\*--------------------------------------------------------------------*/ 
    ProGeomitemFeatureGet(geomitem, &point.feature);

/*--------------------------------------------------------------------*\ 
    Get the point position 
\*--------------------------------------------------------------------*/ 
    ProPointInit(geomitem->owner, geomitem->id, &p);
    ProPointCoordGet(p, point.position);

/*--------------------------------------------------------------------*\ 
    Get the point name 
\*--------------------------------------------------------------------*/ 
    ProModelitemNameGet(geomitem, point.name);

/*--------------------------------------------------------------------*\ 
    Add the point to the array 
\*--------------------------------------------------------------------*/ 
    ProArrayObjectAdd((ProArray*)data, -1, 1, &point);

    return(PRO_TK_NO_ERROR);
 }

/*====================================================================*\ 
FUNCTION : UsrFeatureAction() 
PURPOSE  : Visit action function called for each feature
\*====================================================================*/ 
ProError UsrFeatureAction(
     ProFeature *feature,
     ProError filt_status,
     ProAppData data)
 {
     ProFeatStatus fstatus;

/*--------------------------------------------------------------------*\ 
    If the feature is not active, skip it 
\*--------------------------------------------------------------------*/ 
     ProFeatureStatusGet(feature, &fstatus);
     if(fstatus != PRO_FEAT_ACTIVE)
         return(PRO_TK_NO_ERROR);

/*--------------------------------------------------------------------*\ 
    Visit the datum points in the feature 
\*--------------------------------------------------------------------*/ 
    ProFeatureGeomitemVisit(feature, PRO_POINT, UsrPointAction, 
                              NULL, data);

    return(PRO_TK_NO_ERROR);
 }

/*====================================================================*\ 
FUNCTION : UsrPointsCollect() 
PURPOSE  : Collect an array of datum points in the solid 
\*====================================================================*/ 
int UsrPointsCollect(
     ProSolid solid,
     Point_t **points) { 
/*--------------------------------------------------------------------*\ 
    Allocate the array 
\*--------------------------------------------------------------------*/ 
    ProArrayAlloc(0, sizeof(Point_t), 1, (ProArray*)points);

/*--------------------------------------------------------------------*\ 
    Visit the features 
\*--------------------------------------------------------------------*/ 
    ProSolidFeatVisit(solid, UsrFeatureAction, NULL, points);
 }