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

#include <GearDesign.h>

#include <ProToolkit.h>
#include <Pro2dEntdef.h>
#include <ProFeature.h>
#include <ProModelitem.h>

#include <UtilCollect.h>
#include <UtilMath.h>
#include <ProSection.h>

#include "UtilMenu.h"
#include "UtilMessage.h"
#include "TestSect.h"
#include "UtilMessage.h"
#include "TestError.h"
#include "UtilMath.h"
#include "UtilMatrix.h"
#include "UtilCollect.h"
#include "UtilString.h"

#include <ProToolkit.h>
#include <ProObjects.h>
#include <ProSecdim.h>
#include <ProSection.h>
#include <ProMdl.h>
#include <ProMenu.h>
#include <ProEdge.h>
#include <ProGeomitem.h>
#include <ProFeature.h>
#include <ProSelection.h>
#include <ProSurface.h>
#include <ProArray.h>
#include <ProUtil.h>
#include <ProCurve.h>
#include <ProTKRunTime.h>
#include <ProAxis.h>

#define sqr(a) ((a)*(a))



typedef struct feat_by_name_find
{
   FeatByName *feats;
   int num_feats;	 
}  FeatByNameFind;

static ProError err;

/*=============================================================*\
  Function: 	ProUtilCreateCircleSection
  Purpose:	Full creating of section with one circle
  Return :      
\*=============================================================*/
ProError ProUtilCreateCircleSection(
    ProSection section,		/*In : the section */
    Parameter *params)		/*In : params [0] - diameter */
				/*     params [1] - vertical surface */
				/*     params [2] - vertical surface */
{
    Pro2dCircledef circle;
    Pro2dEntdef* ent;
    Pro2dLinedef* line;
    Pro2dPnt     point;
    int brk=0;
    int circle_id, circle_diam, side_id, bot_id;
    ProSectionPointType pnt_types[2];
    ProError err;
    ProWSecerror errors;

    do /* Used for exit from the middle of block */
      {
/*----------------------------------------------------------*\
    Create entities
\*----------------------------------------------------------*/

	circle.type = PRO_2D_CIRCLE;
	
	/* Align the X coordinate of the circle with the side datum plane */
	err = ProSectionEntityFromProjection(section, params[1].r, &side_id);  
	err = ProSectionEntityGet (section, side_id, &ent);	
	line = (Pro2dLinedef*) ent;	
	circle.center[0] = line->end1 [0];
	
	/* Align the Y coordinate of the circle with the bottom datum plane */
	err = ProSectionEntityFromProjection(section, params[2].r, &bot_id);   
	err = ProSectionEntityGet (section, bot_id, &ent);	
	line = (Pro2dLinedef*) ent;	
	circle.center[1] = line->end1 [1];
	
	circle.radius = params[0].d/2;
	
	err = ProSectionEntityAdd(section, (Pro2dEntdef*)&circle, &circle_id);    
/*----------------------------------------------------------*\
    Add dimensions
\*----------------------------------------------------------*/
	/*point[0] = 0.0;
	point[1] = params[0].d/2+1; 
	pnt_types[0] = PRO_ENT_WHOLE;
	err =ProSecdimCreate(section, &circle_id, pnt_types, 1,
			     PRO_TK_DIM_DIA, point, &circle_diam);*/

/*----------------------------------------------------------*\
    Solve section
\*----------------------------------------------------------*/
	err = ProSecerrorAlloc(&errors);
	err = ProSectionAutodim (section, &errors);			   
	err = ProSecerrorFree(&errors);
    } while (brk);
    return (err);
}
/*=============================================================*\
  Function: 	ProUtilCreateToothSection
  Purpose:	Full creating of tooth space section
  Return :      
\*=============================================================*/
ProError ProUtilCreateToothSection(
    ProSection section,		/*In : the section */
    Parameter *params)		/*In : params [0] - the module m */
				/*     params [1] - the number of teeth */
				/*     params [2] - standart pressure angle */
				/*     params [3] - gear side surface */
				/*     params [4] - side datum plane*/
{
    Pro2dLinedef line;
    Pro2dPnt     point, p1, p2, p3;
    Pro2dCircledef *p_circle, add_circle, ded_circle, t_circle;
    Pro2dArcdef arc;
    int dims[2], brk=0, ring_gear, z, arc1_id, arc2_id, d_id;
    int circle_id, ded_id;
    ProSectionPointType pnt_types[2];
    ProError err;
    ProWSecerror errors;
    double d, r, angle, r_base, r_t, m, an ;

    do /* Used for exit from the middle of block */
    {

	err = ProSectionEntityFromProjection(section, params[3].r, &circle_id);
	if(err != PRO_TK_NO_ERROR)
	break;  
	
	err = ProSectionEntityGet(section, circle_id, (Pro2dEntdef**)&p_circle);
	if(err != PRO_TK_NO_ERROR)
	break;  

	m = params[0].d;		 /* module */
	r = p_circle->radius;            /* addendum circle radius */
	z = params[1].i;                 /* no. of teeth */
	d = params[0].d * z;;            /* The Pitch circle radius */

	ring_gear = r < d/2;
	angle = params[2].d*PI/180;
	r_t = d/2 * sin(angle);
	r_base = d/2 * cos(angle); /* The base-circle diam */

	if (ring_gear)
	{
	    add_circle.radius = r;
	    ded_circle.radius = d/2 + 1.2*m;
	    angle = PI/2 + angle - PI/(z*2);
	}
	else
	{
	    add_circle.radius = r;
	    ded_circle.radius = d/2 - 1.25*m;
	    angle = PI/2 - angle - PI/(z*2);
	}
	add_circle.center[0] = ded_circle.center[0] = p_circle -> center [0];
	add_circle.center[1] = ded_circle.center[1] = p_circle -> center [1];

	t_circle.center[0] = p_circle -> center [0] + r_base * cos(angle);
	t_circle.center[1] = p_circle -> center [1] + r_base * sin(angle);
	t_circle.radius = r_t;

	ProUtilTwoCircleIntersection(&add_circle, &t_circle, p1, p2);
	if (ring_gear)
	{
	    if (p1[0]<p2[0])
		memcpy(p1, p2, sizeof(p1));
	} else 
	{
	    if (p1[0]>p2[0])
		memcpy(p1, p2, sizeof(p1));
	}

	ProUtilTwoCircleIntersection(&ded_circle, &t_circle, p2, p3);
	if (ring_gear)
	{
	    if (p2[0]<p3[0])
		memcpy(p2, p3, sizeof(p1));
	} else 
	{
	    if (p2[0]>p3[0])
		memcpy(p2, p3, sizeof(p1));
	}

/*----------------------------------------------------------*\
    Create entities
\*----------------------------------------------------------*/
	line.type = PRO_2D_CENTER_LINE;
	line.end1[0] = p_circle -> center [0];
	line.end1[1] = p_circle -> center [1];
	line.end2[0] = p_circle -> center [0];
	line.end2[1] = p_circle -> center [1] + r;
	err = ProSectionEntityAdd(section, (Pro2dEntdef*)&line, &d_id); 
	if(err != PRO_TK_NO_ERROR)
	break;   

	arc.type =  PRO_2D_ARC;
	memcpy(arc.center, t_circle.center, sizeof(arc.center));
	arc.start_angle = atan2(p1[1]-arc.center[1], p1[0]-arc.center[0]);
	arc.end_angle = atan2(p2[1]-arc.center[1], p2[0]-arc.center[0]);
	arc.radius = r_t;
	err = ProSectionEntityAdd(section, (Pro2dEntdef*)&arc, &arc1_id);   
	if(err != PRO_TK_NO_ERROR)
	break;   

	an = PI - arc.start_angle;
	arc.start_angle = PI - arc.end_angle;
	arc.end_angle = an;
	arc.center[0] = (2 * p_circle -> center [0] ) - arc.center[0];
	err = ProSectionEntityAdd(section, (Pro2dEntdef*)&arc, &arc2_id);  
	if(err != PRO_TK_NO_ERROR)
	break;    

	memcpy(arc.center, ded_circle.center, sizeof(arc.center));
	arc.start_angle = atan2( p2[1] - arc.center [1], p2[0] - arc.center [0]);
	arc.end_angle = PI - arc.start_angle;
	arc.radius = ded_circle.radius;
	err = ProSectionEntityAdd(section, (Pro2dEntdef*)&arc, &ded_id);  
	if(err != PRO_TK_NO_ERROR)
	break;    

/*----------------------------------------------------------*\
    Add dimensions
\*----------------------------------------------------------*/
	point[0] = p_circle -> center [0];
	point[1] = t_circle.center[1] + 1; 
	pnt_types[0] = PRO_ENT_CENTER;
	pnt_types[1] = PRO_ENT_CENTER;
	dims[0] = arc1_id;
	dims[1] = arc2_id;
	err = ProSecdimCreate(section, dims, pnt_types, 2,
			     PRO_TK_DIM_PNT_PNT_HORIZ, point, &d_id);
        if(err != PRO_TK_NO_ERROR)
	break;  
	
	point[0] = 1.2 * t_circle.center[0];
	point[1] = t_circle.center[1]/2;
	dims[1] = circle_id;
	err = ProSecdimCreate(section, dims, pnt_types, 2,
			     PRO_TK_DIM_PNT_PNT_VERT, point, &d_id);
        if(err != PRO_TK_NO_ERROR)
	break;  
	
	point[0] = 0;
	if (ring_gear)
	    point[1] = 1.2 * ded_circle.radius;
	else
	    point[1] = 0.8 * ded_circle.radius;
	pnt_types[0] = PRO_ENT_WHOLE;
	err = ProSecdimCreate(section, &ded_id, pnt_types, 1, 
			      PRO_TK_DIM_RAD, point, &d_id);  
	if(err != PRO_TK_NO_ERROR)
	break;  
	    
	point[0] = p1[0] * 1.2;
	point[1] = p1[1] * 1.2;
	err = ProSecdimCreate(section, &arc1_id, pnt_types, 1, 
			      PRO_TK_DIM_RAD, point, &d_id);  
        if(err != PRO_TK_NO_ERROR)
	break;  
/*----------------------------------------------------------*\
    Solve section
\*----------------------------------------------------------*/
    	err = ProSecerrorAlloc(&errors);
	err = ProSectionSolve(section, &errors);
	if (err != PRO_TK_NO_ERROR)
	    break;

	err = ProSecerrorFree(&errors);
    } while (brk);

    return (err);
}
/*=============================================================*\
  Function: 	ProUtilCreateKeySection
  Purpose:	Full creating of open section with tree lines
  Return :      
\*=============================================================*/
ProError ProUtilCreateKeySection(
    ProSection section,		/*In : the section */
    Parameter *params)		/*In : params [0] - section width */
				/*     params [1] - section height */
				/*     params [2] - cyl surface */
{
    Pro2dLinedef line;
    Pro2dPnt     point;
    Pro2dCircledef *p_circle;
    int dims[2], brk=0;
    int left_id, right_id, top_id, width_id, height_id, circle_id;
    ProSectionPointType pnt_types[2];
    ProError err;
    ProWSecerror errors;
    double y, r;


    do /* Used for exit from the middle of block */
    {
        err = ProSectionEntityFromProjection(section, params[2].r, &circle_id);
        err = ProSectionEntityGet(section, circle_id, (Pro2dEntdef**)&p_circle);

        r = p_circle->radius;
        y = sqrt(r*r - params[0].d*params[0].d/4);

/*----------------------------------------------------------*\
    Create entities
\*----------------------------------------------------------*/
        line.type = PRO_2D_LINE;
        line.end1[0] = line.end2[0] = -params[0].d/2;
        line.end1[1] = y;
        line.end2[1] = r + params[1].d;
        err = ProSectionEntityAdd(section, (Pro2dEntdef*)&line, &left_id);

        line.end1[0] = line.end2[0] = -line.end1[0];
        err = ProSectionEntityAdd(section, (Pro2dEntdef*)&line, &right_id);



        line.end1[0] = -line.end1[0];
        line.end1[1] =  line.end2[1];
        err = ProSectionEntityAdd(section, (Pro2dEntdef*)&line, &top_id);

/*----------------------------------------------------------*\
    Add dimensions
\*----------------------------------------------------------*/
        point[0] = 0.0;
        point[1] = r + params[1].d+1;
        pnt_types[0] = PRO_ENT_WHOLE;
        err =ProSecdimCreate(section, &top_id, pnt_types, 1,
                             PRO_TK_DIM_LINE, point, &width_id);

        point[0] = params[0].d/2+1;
        point[1] = (y + point[1])/2;
        pnt_types[0] = pnt_types[1] = PRO_ENT_WHOLE;
        dims[0] = top_id;
        dims[1] = circle_id;
        err = ProSecdimCreate(section, dims, pnt_types, 2,
                              PRO_TK_DIM_LINE_AOC, point, &height_id);

/*----------------------------------------------------------*\
    Solve section
\*----------------------------------------------------------*/
        err = ProSecerrorAlloc(&errors);
        err = ProSectionSolve(section, &errors);

	err = ProSecerrorFree(&errors);
    } while (brk);

    return (err);
}

/*=============================================================*\
  Function: 	ProUtilCreateNotchSection
  Purpose:	Full creating of open section with tree lines
  Return :      
\*=============================================================*/
ProError ProUtilCreateNotchSection(
    ProSection section,		/*In : the section */
    Parameter *params)		/*In : params [0] - circle diameter */
				/*     params [1] - notch radius */
				/*     params [2] - backplate surface */
{
    Pro2dArcdef arc;
    Pro2dCircledef* p_circle;
    Pro2dPnt     point;
    int dims[2], brk=0;
    int circle_id, arc_id, hor_id, vert_id, rad_id;
    ProSectionPointType pnt_types[2];
    ProError err;
    ProWSecerror errors;
    double y, x;


    do /* Used for exit from the middle of block */
    {

	y = ((params[0].d*params[0].d)/2-params[1].d*params[1].d)/params[0].d;
	x = sqrt((params[0].d*params[0].d)/4-y*y);
/*----------------------------------------------------------*\
    Create entities
\*----------------------------------------------------------*/
	err = ProSectionEntityFromProjection(section, params[2].r, &circle_id);
	err = ProSectionEntityGet (section, circle_id, (Pro2dEntdef**)&p_circle);

	arc.type = PRO_2D_ARC;
	arc.center[0] = p_circle -> center [0];
	arc.center[1] = p_circle -> center [1] + params[0].d/2;
	arc.radius = params[1].d;
	arc.start_angle = atan2(y-params[0].d/2, -x)+PI*2;
	arc.end_angle = 3*PI - arc.start_angle;
	err = ProSectionEntityAdd(section, (Pro2dEntdef*)&arc, &arc_id);    

/*----------------------------------------------------------*\
    Add dimensions
\*----------------------------------------------------------*/
	point[0] = p_circle -> center [0];
	point[1] = p_circle -> center [1] + params[0].d/2 ; 
	pnt_types[0] = PRO_ENT_WHOLE;
	err =ProSecdimCreate(section, &arc_id, pnt_types, 1,
			     PRO_TK_DIM_RAD, point, &rad_id);
    
	point[0] = p_circle -> center [0];
	point[1] = p_circle -> center [1] + params[0].d/4;
	pnt_types[0] = PRO_ENT_CENTER;
	pnt_types[1] = PRO_ENT_CENTER;
	dims[0] = circle_id;
	dims[1] = arc_id;
	err = ProSecdimCreate(section, dims, pnt_types, 2, 
			      PRO_TK_DIM_PNT_PNT_HORIZ, point, &hor_id);  

	point[0] = p_circle -> center [0] + 1.0;
	point[1] = p_circle -> center [1] + params[0].d/4;
	pnt_types[0] = PRO_ENT_CENTER;
	pnt_types[1] = PRO_ENT_CENTER;
	dims[0] = circle_id;
	dims[1] = arc_id;
vert_id = -1;
 ProTKPrintf( "point=(%f %f)\n", point[0], point[1] );
	err = ProSecdimCreate(section, dims, pnt_types, 2, 
			      PRO_TK_DIM_AOC_AOC_TAN_VERT, point, &vert_id);  
	    
/*----------------------------------------------------------*\
    Solve section
\*----------------------------------------------------------*/
	err = ProSecerrorAlloc(&errors);
	err = ProSectionSolve(section, &errors);
	if (err != PRO_TK_NO_ERROR)
	    break;

	err = ProSecerrorFree(&errors);
   } while (brk);

    return (err);
}

/*=============================================================*\
  Function: 	ProUtilCreateSketchedFeature
  Purpose:	create a new feature
  Return :      PRO_TK_NO_ERROR if succsessfull, an error otherwise
\*=============================================================*/
ProError ProUtilCreateSketchedFeature(
    GearParam *g_ptr,	    /* In : */    
    FeatureDef *feat_def,   /* In : Feature definition */
    ProFeature *feature)    /* Out: created feature */
{
    static ElemTreeData sketch[]={
	{1, PRO_E_SKETCHER, {PRO_VALUE_TYPE_POINTER}}
    };

    static ProElempathItem path_items[] = {
	{PRO_ELEM_PATH_ITEM_TYPE_ID, PRO_E_STD_SECTION},
	{PRO_ELEM_PATH_ITEM_TYPE_ID, PRO_E_SKETCHER}
    };
    
    ProElement elem_tree, elem_sec;
    ProElement created_elemtree, sketch_element;

    ProError err;
    ProModelitem model_item;
    ProSelection model_sel;
    
    ProFeatureCreateOptions opts[1];
    ProErrorlist errs;
    ProElempath path;
    ProValue value;
    ProValueData value_data;
    ProSection section;
    int brk = 0;

    do /* Used for exit from middle of block */
    {
/*----------------------------------------------------------*\
    Create Element Tree
\*----------------------------------------------------------*/
	err = ProUtilElemtreeCreate(feat_def->tree, feat_def->sizeof_tree, NULL, 
				    &elem_tree);
	if (err != PRO_TK_NO_ERROR)
	    break;

/*----------------------------------------------------------*\
    Create the incomplete protrusion in the current model
\*----------------------------------------------------------*/
	err = ProMdlToModelitem(g_ptr->model, &model_item);
	err = ProSelectionAlloc(NULL, &model_item, &model_sel);
	opts[0] = PRO_FEAT_CR_INCOMPLETE_FEAT;
	err = ProFeatureCreate(model_sel, elem_tree, opts, 1, feature, &errs);
	if (err != PRO_TK_NO_ERROR)
	{
	    ProUtilFeatErrsWrite("ProFeatureCreate", err, elem_tree, &errs);
	    break;
	}
	err = ProSelectionFree(&model_sel);

/*----------------------------------------------------------*\
    Get the initialized section element from the database
\*----------------------------------------------------------*/
	err = ProElempathAlloc(&path);
	err = ProElempathDataSet(path, path_items, 2);

	err = ProFeatureElemtreeCreate (feature, &created_elemtree);
	err = ProElemtreeElementGet (created_elemtree, path, &sketch_element);
	err = ProElementValueGet (sketch_element, &value);

	err = ProValueDataGet(value, &value_data);
	section =  (ProSection)value_data.v.p;
	err = ProElempathFree(&path);
/*----------------------------------------------------------*\
    Create a section
\*----------------------------------------------------------*/
	err = feat_def->section_create_func(section, feat_def->params);
	if (err != PRO_TK_NO_ERROR)
	    break;

/*------------------------------------------------------------*\
    Set section-dependent element values
\*------------------------------------------------------------*/

	ProUtilFeatureSetSectionDependentValues (created_elemtree,
						 feat_def->tree,
						 feat_def->sizeof_tree);

	opts[0] = PRO_FEAT_CR_INCOMPLETE_FEAT;
	err = ProFeatureRedefine(NULL, feature, created_elemtree, opts, 1, &errs);
	if (err != PRO_TK_NO_ERROR)
	{
	    ProUtilFeatErrsWrite("ProFeatureRedefine", err, elem_tree, &errs);
	    break;
	}
	err = ProElementFree(&elem_tree);
/*----------------------------------------------------------*\
    Set feature name
\*----------------------------------------------------------*/
	ProUtilModelitemNameSet(feature, feat_def->feat_name);
    } while (brk);

    return (err);
}

/*=============================================================*\
  Function: 	ProUtilFeatureSetSectionDependentValues
  Purpose:	Assign necessary elements (direction, material side) from 
                original value table (special for Extrude, Revolve element 
	        tree, these elements must be set along with or after 
		PRO_E_SEKTCHER)
\*=============================================================*/
ProError ProUtilFeatureSetSectionDependentValues (ProElement elemtree,
						  ElemTreeData* elemarr,
						  int n_elem)
{
  ElemTreeData* needed_elems = NULL;
  int i_elem, i_value;
  int size;
  
  ProElempath elem_path;
  ProElempathItem path_item;
  ProElement element;
  ProValue value;


  for (i_elem = 0; i_elem < n_elem; i_elem ++)
    {
      switch (elemarr [i_elem].elem_id)
	{
	case PRO_E_EXT_SURF_CUT_SOLID_TYPE:
	  {
	    ProArrayAlloc (0, sizeof (ElemTreeData), 1, (ProArray*)&needed_elems);
	    break;
	  }
	case PRO_E_STD_DIRECTION:
	case PRO_E_STD_MATRLSIDE:
	  {
	    if (needed_elems != NULL)
	      {
		ProArrayObjectAdd ((ProArray*)&needed_elems, -1, 1, &elemarr[i_elem]);
	      }
	    break;
	  }
	default:
	  break;
	}
    }
  if (needed_elems == NULL)
    return PRO_TK_NO_ERROR;

  err = ProArraySizeGet (needed_elems, &size);
  if (err != PRO_TK_NO_ERROR)
    return PRO_TK_NO_ERROR;
  if (size == 0)
    {
      ProArrayFree ((ProArray*)&needed_elems);
      return PRO_TK_NO_ERROR;
    }


  for (i_value = 0; i_value < size; i_value ++)
    {
      switch (needed_elems [i_value].elem_id)
	{
	  case PRO_E_STD_DIRECTION:
	  case PRO_E_STD_MATRLSIDE:
	    {
              ProElempathAlloc (&elem_path);
	      path_item.type = PRO_ELEM_PATH_ITEM_TYPE_ID;
	      path_item.path_item.elem_id = needed_elems [i_value].elem_id;
	    
	      ProElempathDataSet (elem_path, &path_item, 1);

	      ProElemtreeElementGet (elemtree, elem_path, &element);
	      
	      /*----------------------------------------------------------*\
		Add Element value
	      \*----------------------------------------------------------*/
              ProValueAlloc(&value);
            
              ProValueDataSet(value, &needed_elems[i_value].data);
            
              ProElementValueSet(element, value);
              ProElempathFree (&elem_path);
	       
	      break;
	    }
	}
    }

  ProArrayFree ((ProArray*) &needed_elems);

  return PRO_TK_NO_ERROR;
}

/*=============================================================*\
  Function: 	ProUtilModelitemNameSet
  Purpose:	Set new name for the model item
  Return :      as ProModelitemNameSet
\*=============================================================*/
ProError ProUtilModelitemNameSet(ProModelitem *modelitem, char *name)
{
    ProName w_name;
    ProError err;

    ProStringToWstring(w_name, name);
    err = ProModelitemNameSet(modelitem, w_name);
    return (err);
}

/*=============================================================*\
  Function: 	ProUtilFeatByNameVisit
  Purpose:	visiting fucntion for ProSolidFeatVisit
  Return :      1 if feature with required name found, 0 otherwise
\*=============================================================*/
static ProError ProUtilModelitemByNameVisit(
    ProModelitem *modelitem, 
    ProError err, 
    ProAppData app_data)
{
    FeatByNameFind *feat_by_name = (FeatByNameFind *)app_data;
    ProName   name;	
    int i;
    ProBoolean vis;

    if (modelitem->type == PRO_FEATURE)
    {
	err = ProFeatureVisibilityGet((ProFeature*)modelitem, &vis);
	if (err!=PRO_TK_NO_ERROR || vis == PRO_B_FALSE)
	    return (PRO_TK_NO_ERROR);
    }
    
    err = ProModelitemNameGet(modelitem, name);
    if (err != PRO_TK_NO_ERROR)
	return (PRO_TK_NO_ERROR);

    for (i=0; i<feat_by_name->num_feats; i++)
    {
	if (ProUtilStrwscmp(feat_by_name->feats[i].name, name)==0)
	    feat_by_name->feats[i].id = modelitem->id;
    }
    return (PRO_TK_NO_ERROR);
}


/*=============================================================*\
  Function: 	ProUtilFeatsByName
  Purpose:	to find solid features by name
  Return :      PRO_TK_NO_ERROR if successfull,
\*=============================================================*/
ProError ProUtilFeatsByName(
    ProSolid model,
    FeatByName *feats,
    int num_feats)
{
    FeatByNameFind feat_by_name;
    ProError   err;
    int i;

    feat_by_name.feats = feats;
    feat_by_name.num_feats = num_feats;
    for (i=0; i<num_feats; i++)
	feats[i].id = -1;
    
    err = ProSolidFeatVisit((ProSolid)model, 
	(ProFeatureVisitAction)ProUtilModelitemByNameVisit, NULL, 
	(ProAppData)&feat_by_name);
    return (err);
}

/*=============================================================*\
  Function: 	ProUtilSelectionFromSurfaceId
  Purpose:	transformation from surface id to ProSelection
  Return :      PRO_TK_NO_ERROR if successfull,
\*=============================================================*/
ProError ProUtilSelectionFromSurfaceId(
    ProMdl model,	/* In : model */
    int surf_id,	/* In : surface id */
    ProSelection *sel)	/* Out: selection, to free use ProSelectionFree() */
{
    ProError err;
    ProSurface surf;
    ProModelitem modelitem;
    
    err=ProSurfaceInit(model, surf_id, &surf);
    if (err!=PRO_TK_NO_ERROR)
	return (err);
    err = ProSurfaceToGeomitem((ProSolid)model, surf, (ProGeomitem*)&modelitem);
    if (err != PRO_TK_NO_ERROR)
    	return (err);
    err = ProSelectionAlloc(NULL, &modelitem, sel);
    if (err !=  PRO_TK_NO_ERROR)
    	return (err);
    
    
    return (PRO_TK_NO_ERROR);
}

/*=============================================================*\
  Function: 	ProUtilAxisGeomitemVisit
  Purpose:	Visit function for the ProFeatureGeomitemVisit
  Return :      only one axis as feature it self is axis 
\*=============================================================*/
ProError ProUtilAxisGeomitemVisit(
    ProGeomitem *geomitem,
    ProError err,
    ProAppData appdata)
{
    ProGeomitem *axis =  (ProGeomitem*)appdata;
    axis[0] = geomitem[0];
    err = PRO_TK_E_FOUND;
    return (PRO_TK_E_FOUND);
}

/*=============================================================*\
  Function: 	ProUtilAxisGeomitem
  Purpose:	Found Axis GeomItem for the axis feature (PRO_FEAT_DATUM_AXIS)
  Return :      PRO_TK_NO_ERROR if successful
\*=============================================================*/
ProError ProUtilAxisGeomitem(
    ProFeature *feature_axis,	    /* In: the feature */ 
    ProType type,		    /* In: Geomitem type */
    int  *featid)		    /*Out: Geomitem id  */
{
    ProModelitem modelitem;
    ProError err;

    err = ProFeatureGeomitemVisit(feature_axis, type, 
	                          ProUtilAxisGeomitemVisit, NULL, 
				  (ProAppData)&modelitem);

    if (err==PRO_TK_E_FOUND)
    {
	featid[0] = modelitem.id;
	return (PRO_TK_NO_ERROR);
    }
    return (PRO_TK_E_NOT_FOUND);
}

/*===================================================================*\
  Function: 	ProUtilSelectionFromAxisId
  Purpose:	transformation from axis id to ProSelection
  Return :      PRO_TK_NO_ERROR if successfull,
\*=============================================================*/

ProError ProUtilSelectionFromAxisId(
    ProMdl model,	/* In : model */
    int axis_id,	/* In : axis id */
    ProSelection *sel)	/* Out: selection, to free use ProSelectionFree() */
{
    ProError err;
    ProAxis axis;
    ProModelitem modelitem;
    
    err = ProAxisInit(model, axis_id, &axis);
    if (err!=PRO_TK_NO_ERROR) 
	return (err);
    err = ProAxisToGeomitem((ProSolid)model, axis, (ProGeomitem*)&modelitem);
    if (err != PRO_TK_NO_ERROR)
    	return (err);
    err = ProSelectionAlloc(NULL, &modelitem, sel);
    if (err!=PRO_TK_NO_ERROR) 
	return (err);
	
    return (PRO_TK_NO_ERROR);
}


/*=============================================================*\
  Function: 	ProUtilGeometryAtPointFind
  Purpose:	to find surface at point
  Return :      PRO_TK_NO_ERROR if successfull,
		PRO_TK_E_NOT_FOUND otherwise
\*=============================================================*/
ProError ProUtilGeometryAtPointFind(
    ProPart part, 
    Pro3dPnt point, 
    ProModelitem *modelitem)
{
    int n_sel, i;
    ProSelection *p_sel;
    ProType type;
    ProError err;

    err = ProGeometryAtPointFind(part, point, &p_sel, &n_sel);
    
    if (err != PRO_TK_NO_ERROR)
	return (PRO_TK_E_NOT_FOUND);

    err = ProSelectionModelitemGet(p_sel[0], modelitem);
    
    /* Free memory allocated by ProGeometryAtPointFind */
    for (i = 0; i < n_sel; i++)
        ProSelectionFree( &p_sel[i]);

    ProArrayFree((ProArray *) &p_sel);
    return (PRO_TK_NO_ERROR);
}



/*=============================================================*\
  Function: 	ProUtilFeatFirstGeomitemVisit
  Purpose:	Visit function for the ProFeatureGeomitemVisit
  Return :      1 (only first geom item visited)
\*=============================================================*/
ProError ProUtilFeatFirstGeomitemVisit(
    ProGeomitem *geom_item,
    ProError err,
    ProAppData app_data)
{
    ProGeomitem *surf =  (ProGeomitem*)app_data;
    surf[0] = geom_item[0];
    return ((ProError)1);
}

/*=============================================================*\
  Function: 	ProUtilFeatFirstGeomitem
  Purpose:	Found first GeomItem for the feature
  Return :      PRO_TK_NO_ERROR if successful
\*=============================================================*/
ProError ProUtilFeatFirstGeomitem(
    ProFeature *feature,	    /* In: the feature */ 
    ProType type,		    /* In: Geomitem type */
    int  *item_id)		    /*Out: Geomitem id, user's memory */
{
    ProModelitem model_item;
    ProError err;

    err = ProFeatureGeomitemVisit(feature, type, 
	ProUtilFeatFirstGeomitemVisit, NULL, (ProAppData)&model_item);

    if (err==1)
    {
	item_id[0] = model_item.id;
	return (PRO_TK_NO_ERROR);
    }
    return (PRO_TK_E_NOT_FOUND);
}

/*=============================================================*\
  Function: 	ProUtilFeatFirstDimensionVisit
  Purpose:	Visit function for the ProFeatureDimensionVisit
  Return :      1 (only first dim visited)
\*=============================================================*/
ProError ProUtilFeatFirstDimensionVisit(
    ProDimension *dim,
    ProError err,
    ProAppData app_data)
{
    ProDimension *found =  (ProDimension*)app_data;
    found[0] = dim[0];
    return ((ProError)1);
}

/*=============================================================*\
  Function: 	ProUtilFeatFirstDimension
  Purpose:	Found first GeomItem for the feature
  Return :      PRO_TK_NO_ERROR if successful
\*=============================================================*/
ProError ProUtilFeatFirstDimension(
    ProFeature *feature,	    /* In: the feature */ 
    int  *item_id)		    /*Out: Dimension id, user's memory */
{
    ProModelitem model_item;
    ProError err;

    err = ProFeatureDimensionVisit(feature, 
	ProUtilFeatFirstDimensionVisit, NULL, (ProAppData)&model_item);

    if (err==1)
    {
	item_id[0] = model_item.id;
	return (PRO_TK_NO_ERROR);
    }
    return (PRO_TK_E_NOT_FOUND);
}

/*=============================================================*\
  Function: 	ProUtilFindTwoCircleIntersection
  Purpose:	Calculate intersection of two circles
  Return :      PRO_TK_NO_ERROR if successfull
		PRO_TK_GENERAL_ERROR - no intersection
\*=============================================================*/
ProError ProUtilTwoCircleIntersection(
    Pro2dCircledef *c1, 
    Pro2dCircledef *c2, 
    Pro2dPnt	    p1,
    Pro2dPnt	    p2)
{
    double d, x1, y1, sa, ca;

    d = sqrt(sqr(c1->center[0]-c2->center[0]) + 
             sqr(c1->center[1]-c2->center[1]));
    if ((d<EPSM6) || (d>c1->radius + c2->radius) || 
	(d + c1->radius < c2->radius) || (d + c2->radius < c1->radius))
	return (PRO_TK_GENERAL_ERROR);

    y1 = (sqr(c1->radius) + sqr(d) - sqr(c2->radius)) / 2 /d;
    x1 = sqrt(sqr(c1->radius) - sqr(y1));

    sa = (c2->center[0]-c1->center[0]) / d;
    ca = (c2->center[1]-c1->center[1]) / d;

    p1[0] = y1 * sa + x1 * ca + c1->center[0];
    p1[1] = y1 * ca - x1 * sa + c1->center[1];
    p2[0] = y1 * sa - x1 * ca + c1->center[0];
    p2[1] = y1 * ca + x1 * sa + c1->center[1];
    
    return (PRO_TK_NO_ERROR);
}


/*=============================================================*\
  Function: 	ProUtilFeatErrsWrite
  Purpose:	Debug function. Print elem tree and errors af feature 
                creation or redefenition failed
*=============================================================*/
void ProUtilFeatErrsWrite(
    char *function,
    ProError err,
    ProElement tree,
    ProErrorlist *err_list)
{
    ProTKFprintf(stderr, "Function %s returned %d\n", function, err);
    ProUtilElementtreePrint(tree, (UtilTreeprintWindow)2, 0);
    ProUtilFeatErrsPrint(err_list);
}

ProError ProUtilSecEntityUseCurveLoop (ProSection section, Parameter *params)
{


    ProSelection *ref_geom_sketch;
    ProModelitem ref_item;
    ProSelection curve_ref;
    ProIntlist ids;
    ProWSecerror sec_errors;
    int num_ids;
    ProErrorlist errors;
    ProMdl model;
    ProError status;
    int curve_id;
    ProFeature feature;
    ProModelitem app_data;
    FeatByName feat_sketch[] = {
                                    {"SKETCH_1"}};

    ProCurve curve;
    CurveComponent *curvecomps;

    status = ProMdlCurrentGet (&model);

    status = ProUtilFeatsByName(model, feat_sketch, 1);
    
    status = ProFeatureInit(model, feat_sketch[0].id, &feature);
    
    status = ProFeatureGeomitemVisit(&feature, PRO_CURVE, ProUtilGeomItemVisitCurve, NULL, (ProAppData)&app_data);
    
    curve_id = app_data.id;
    
    // Extract curve from composite curve
    status = ProCurveInit ((ProSolid)model, curve_id, &curve);
    ProUtilCollectCurveComponents(curve, &curvecomps);
    status = ProCurveIdGet(curvecomps[0].p_curve, &curve_id);
    status = ProModelitemInit (model, (curve_id), PRO_CURVE, &ref_item);
    
    status = ProSelectionAlloc (NULL, &ref_item, &curve_ref);

    status = ProSectionEntityUseCurveLoop (section, curve_ref, &ids,  &num_ids);

    status = ProSelectionFree (&curve_ref);

    status = ProSecerrorAlloc (&sec_errors);

    status = ProSectionAutodim (section, &sec_errors);

    status = ProSecerrorFree (&sec_errors);

    return (status);
}

ProError ProUtilGeomItemVisitCurve (ProGeomitem *geom_item, ProError err_status, ProAppData data)
{
    ProGeomitem *curve =  (ProGeomitem*)data;
    curve[0] = geom_item[0];
    err_status = PRO_TK_E_FOUND;
    return (PRO_TK_E_FOUND);
}