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

#include <ProToolkit.h>
#include <ProDtmPln.h>
#include <ProDtmCsys.h>
#include <ProElemId.h>
#include <ProFeatType.h>
#include <ProMenu.h>
#include <ProMessage.h>
#include <ProMdl.h>
#include <ProUtil.h>


#include <TestError.h>
#include <UtilCollect.h>
#include <UtilMath.h>
#include <UtilMenu.h>
#include <UtilMessage.h>
#include <UtilTree.h>
#include "UtilString.h"


#define msgfil "feat"
#define SIZEOFARR(a) (sizeof(a)/(sizeof(a[0])))


#define MAX_CONSTR_TYPES 7
#define MAX_REF_TYPE 5

typedef enum dtm_constr_type
{
    DCTR_THROUGH = 0,
    DCTR_NORMAL,
    DCTR_PARALLEL,
    DCTR_OFFSET,
    DCTR_ANGLE,
    DCTR_TANGENT,
    DCTR_BLEND_SECTION,
    DCTR_DEFAULT
} DtmConstrType;

typedef enum dtm_constr_ref
{
    USER_LINE =  0,
    USER_POINT,
    USER_PLANE,
    USER_CYL,
    USER_CSYS
} DtmConsrRef;

typedef struct test_ref_type {
    char *button_name;
    char *select_option;
    int freedom[MAX_CONSTR_TYPES];

    int state_en;
    int state_hi;
} RefType;

typedef struct test_constr_type {
    char *button_name;

    int state_en;
} ContrType;

typedef struct dtmpln_constr {
    int cur_constr;

    int constr_enabled;
    int ref_highlighted;
    int ref_enabled;

    ProBoolean	ref_pressed;


    Pro3dPnt	points[3];
    int		n_points;
    ProBoolean	through;

    int n_constr;
    ProDtmplnConstrType type[3];
    ProSelection ref[3];
    double  offset[3];
    int	    index[3];
    int	    freedom;

    ProMdl  model;
} DtmPlnConstr;

static ContrType constr_type[MAX_CONSTR_TYPES] = {
    {(char *)"Through"},
    {(char *)"Normal"},
    {(char *)"Parallel"},
    {(char *)"Offset"},
    {(char *)"Angle"},
    {(char *)"Tangent"},
    {(char *)"BlendSection"}
};

static RefType ref_type[MAX_REF_TYPE] = {
    {(char *)"AxisEdgeCurv", (char *)"axis,edge,curve", { 2, 1, -1, -1, -1, -1, -1}}, 
    {(char *)"Point/Vertex", (char *)"point,edge_end,curve_end", { 1, -1, -1, -1, -1, -1, -1}},
    {(char *)"Plane", (char *)"surface,datum", { 3, 1, 1, 3, 1, -1, -1}},
    {(char *)"Cylinder", (char *)"surface", { 2, -1, -1, -1, -1, 1, -1}},
    {(char *)"Coord Sys", (char *)"csys", {-1, -1, -1, 3, -1, -1, -1}}
};

static DtmPlnConstr constraint;


/*=============================================================*\
  FUNCTION: ProUtilDefMenuAction
  PURPOSE:  Menu button function
\*=============================================================*/
static int ProUtilDefMenuAction(
    ProAppData data,
    int action)
{
    ProError err;

    err = ProMenuDeleteWithStatus(action);
    TEST_CALL_REPORT("ProFeatureTypeGet()",
		    "ProUtilDefMenuaAction()", err, err != PRO_TK_NO_ERROR);
    return (0);
}

/*=============================================================*\
  FUNCTION: ProTestDtmplnDefCreate
  PURPOSE:  Create default datum plane
\*=============================================================*/
ProError ProTestDtmplnDefCreate(
    ProMdl model, 
    int n_csys)
{
    ProError err;
    int action, n_sel, i;
    ProElement tree, constr_elem;
    ProSelection *p_sel_arr, csys_sel = NULL, featsel;
    ProModelitem modelitem;
    ProGeomitem *p_geomitems;
    ProFeature feat;
    ProErrorlist   errs;
    ProFeatureCreateOptions *opts = 0;
    char *c= (char *)"x";
    double offset[3];
    static ElemTreeData def_csys[]= {
    	{0, PRO_E_FEATURE_TREE, {(ProValueDataType)-1}},
	{1, PRO_E_FEATURE_TYPE, {PRO_VALUE_TYPE_INT, {PRO_FEAT_CSYS}}},
	{1, PRO_E_CSYS_METHOD, {PRO_VALUE_TYPE_INT, {PRO_CSYS_DEFAULT}}}
    };
    static ElemTreeData dtmpln[] = {
	{0, PRO_E_FEATURE_TREE, {(ProValueDataType)-1}},
	{1, PRO_E_FEATURE_TYPE, {PRO_VALUE_TYPE_INT, {PRO_FEAT_DATUM}}},
	{1, PRO_E_DTMPLN_CONSTRAINTS, {(ProValueDataType)-1}},
	{2, PRO_E_DTMPLN_CONSTRAINT, {(ProValueDataType)-1}}
    };
    static ElemTreeData def_dtmpln[] = {
	{1, PRO_E_DTMPLN_CONSTR_TYPE, {PRO_VALUE_TYPE_INT, {PRO_DTMPLN_DEF_X}}}
    };
    static ElemTreeData offs_dtmpln[] = {
	{1, PRO_E_DTMPLN_CONSTR_TYPE, {PRO_VALUE_TYPE_INT, {PRO_DTMPLN_OFFS}}},
	{1, PRO_E_DTMPLN_CONSTR_REF, {PRO_VALUE_TYPE_SELECTION}}, 
	{1, PRO_E_DTMPLN_OFF_CSYS, {PRO_VALUE_TYPE_INT}},
	{1, PRO_E_DTMPLN_OFF_CSYS_OFFSET, {PRO_VALUE_TYPE_DOUBLE}}
    };
    static ProElempathItem constr_path[] = {
	{PRO_ELEM_PATH_ITEM_TYPE_ID, PRO_E_DTMPLN_CONSTRAINTS},
	{PRO_ELEM_PATH_ITEM_TYPE_INDEX, 0}
    };
    static int offs_type[3] = {PRO_DTMPLN_OFF_CSYS_X, PRO_DTMPLN_OFF_CSYS_Y, 
	PRO_DTMPLN_OFF_CSYS_Z
    };
    static int def_constr_type[3] = {PRO_DTMPLN_DEF_X, PRO_DTMPLN_DEF_Y,
	PRO_DTMPLN_DEF_Z
    };

    err = ProMenuFileRegister((char *)"tkmenudtm opt",(char *)"tkmdtmopt.mnu", NULL);
    err = ProMenubuttonActionSet((char *)"tkmenudtm opt", (char *)"Default",
	(ProMenubuttonAction)ProUtilDefMenuAction, NULL, DCTR_DEFAULT);
    err = ProMenubuttonActionSet((char *)"tkmenudtm opt", (char *)"Offset",
	(ProMenubuttonAction)ProUtilDefMenuAction, NULL, DCTR_OFFSET);
    err = ProMenubuttonActionSet((char *)"tkmenudtm opt", (char *)"tkmenudtm opt",
	(ProMenubuttonAction)ProMenuDelete, NULL, 0);

    err = ProMenuCreate(PROMENUTYPE_MAIN, (char *)"tkmenudtm opt", NULL);
    err = ProMenuProcess((char *)"", &action);

    if (err != PRO_TK_NO_ERROR)
	return (err);

    if (action != DCTR_DEFAULT)
    {
	if (n_csys > 0)
	{
/*------------------------------------------------------------------*\
	Csys alredy exists, select one
\*------------------------------------------------------------------*/
	    ProUtilMsgPrint(msgfil, (char *)"TEST Select a coordinate system.");
	    err = ProSelect((char *)"csys", 1, NULL, NULL, NULL, NULL, &p_sel_arr, 
		&n_sel);
	    TEST_CALL_REPORT("ProSelect()",
		    "ProTestDtmplnDefCreate()", err, err != PRO_TK_NO_ERROR);
	    if (err != PRO_TK_NO_ERROR)
		return (err);

	    err = ProSelectionCopy(p_sel_arr[0], &csys_sel);
	    TEST_CALL_REPORT("ProSelectionCopy()",
		    "ProTestDtmplnDefCreate()", err, err != PRO_TK_NO_ERROR);
	}
	
	for (i=0, c[0]='x'; i<3; i++, c[0]++)
	{
/*------------------------------------------------------------------*\
	Read offset values
\*------------------------------------------------------------------*/
	    offset[i] = 0;
	    ProUtilMsgPrint(msgfil, 
		(char *)"TEST Enter offset value in the %0s-direction [%1f]:",
		 c, offset+i);

	    err = ProMessageDoubleRead(NULL, offset+i);
	    TEST_CALL_REPORT("ProMessageDoubleRead()",
		    "ProTestDtmplnDefCreate()", err, err != PRO_TK_NO_ERROR);
	     
	     if (err == PRO_TK_MSG_USER_QUIT)
		break;
	}
	if (i<3)
	    return (PRO_TK_NO_ERROR);

	if (n_csys == 0)
	{
/*------------------------------------------------------------------*\
	Create default datum csys first if no one already created
\*------------------------------------------------------------------*/
	    err = ProUtilElemtreeCreate(def_csys, SIZEOFARR(def_csys), NULL, 
		&tree);
    
	    err = ProMdlToModelitem(model, &modelitem);
	    TEST_CALL_REPORT("ProMdlToModelitem()",
		    "ProTestDtmplnDefCreate()", err, err != PRO_TK_NO_ERROR);
	    err = ProSelectionAlloc(NULL, &modelitem, &featsel);
	    TEST_CALL_REPORT("ProSelectionAlloc()",
		    "ProTestDtmplnDefCreate()", err, err != PRO_TK_NO_ERROR);
	    err = ProArrayAlloc(1,sizeof(ProFeatureCreateOptions),
	    	    1, (ProArray*)&opts);

	    opts[0]= PRO_FEAT_CR_NO_OPTS;

	    err = ProFeatureWithoptionsCreate(featsel, tree,
	    	    opts, PRO_REGEN_NO_FLAGS, &feat, &errs);	    
	    TEST_CALL_REPORT("ProFeatureWithoptionsCreate()",
		    "ProTestDtmplnDefCreate()", err, err != PRO_TK_NO_ERROR);

	    err = ProArrayFree((ProArray*)&opts);
	    err = ProSelectionFree(&featsel);
	    TEST_CALL_REPORT("ProSelectionFree()",
		    "ProTestDtmplnDefCreate()", err, err != PRO_TK_NO_ERROR);
	    err = ProUtilCollectFeatureGeomitems(&feat, PRO_CSYS, &p_geomitems);
	    if (err != PRO_TK_NO_ERROR)
		return(err);
	    err = ProSelectionAlloc(NULL, p_geomitems, &csys_sel);
	    TEST_CALL_REPORT("ProSelectionAlloc()",
		    "ProTestDtmplnDefCreate()", err, err != PRO_TK_NO_ERROR);
	    err = ProArrayFree((ProArray*)&p_geomitems);
	    TEST_CALL_REPORT("ProArrayFree()",
		    "ProTestDtmplnDefCreate()", err, err != PRO_TK_NO_ERROR);
	    err = ProElementFree(&tree);
	    TEST_CALL_REPORT("ProElementFree()",
		    "ProTestDtmplnDefCreate()", err, err != PRO_TK_NO_ERROR);
    	}
    }

/*------------------------------------------------------------------*\
	Alloc selection to the model
\*------------------------------------------------------------------*/
    err = ProMdlToModelitem(model, &modelitem);
    TEST_CALL_REPORT("ProMdlToModelitem()",
		    "ProTestDtmplnDefCreate()", err, err != PRO_TK_NO_ERROR);
    err = ProSelectionAlloc(NULL, &modelitem, &featsel);
    TEST_CALL_REPORT("ProSelectionAlloc()",
		    "ProTestDtmplnDefCreate()", err, err != PRO_TK_NO_ERROR);

    for (i=0; i<3; i++)
    {
/*------------------------------------------------------------------*\
	Create main part of feat tree
\*------------------------------------------------------------------*/
	err = ProUtilElemtreeCreate(dtmpln, SIZEOFARR(dtmpln), NULL, &tree);

	err = ProUtilElemtreeElementGet(tree, constr_path, 
	    SIZEOFARR(constr_path), &constr_elem);

/*------------------------------------------------------------------*\
	Create specific part of feat tree
\*------------------------------------------------------------------*/
	if (action == DCTR_DEFAULT)
	{
	    def_dtmpln[0].data.v.i = def_constr_type[i];
	    err = ProUtilElemtreeCreate(def_dtmpln, SIZEOFARR(def_dtmpln), 
		constr_elem, &constr_elem);
	}
	else
	{
	    offs_dtmpln[1].data.v.r = csys_sel;
	    offs_dtmpln[2].data.v.i = offs_type[i];
	    offs_dtmpln[3].data.v.d = offset[i];
	    err = ProUtilElemtreeCreate(offs_dtmpln, SIZEOFARR(offs_dtmpln), 
		constr_elem, &constr_elem);
	}

/*------------------------------------------------------------------*\
	Create datum plane
\*------------------------------------------------------------------*/
	err = ProArrayAlloc(1,sizeof(ProFeatureCreateOptions),
		1, (ProArray*)&opts);

	opts[0]= PRO_FEAT_CR_NO_OPTS;

	err = ProFeatureWithoptionsCreate(featsel, tree,
		opts, PRO_REGEN_NO_FLAGS, &feat, &errs);	
	TEST_CALL_REPORT("ProFeatureWithoptionsCreate()",
		    "ProTestDtmplnDefCreate()", err, err != PRO_TK_NO_ERROR);

	err = ProArrayFree((ProArray*)&opts);

	err = ProElementFree(&tree);
	TEST_CALL_REPORT("ProElementFree()",
		    "ProTestDtmplnDefCreate()", err, err != PRO_TK_NO_ERROR);
    }
    
    err = ProSelectionFree(&featsel);
    TEST_CALL_REPORT("ProSelectionFree()",
		    "ProTestDtmplnDefCreate()", err, err != PRO_TK_NO_ERROR);
    if (csys_sel != NULL)
    {
	err = ProSelectionFree(&csys_sel);
	TEST_CALL_REPORT("ProSelectionFree()",
			"ProTestDtmplnDefCreate()", err, err != PRO_TK_NO_ERROR);
    }

    ProUtilMsgPrint(msgfil, (char *)"TEST %0s has been created successfully.", 
	"DATUM PLANE");
    return(PRO_TK_NO_ERROR);
}

/*=============================================================*\
  FUNCTION: ProUtilCheckFeatures
  PURPOSE:  Visit func. Check if another than csys feature is created
\*=============================================================*/
ProError ProUtilNonCsysFeatCheck(
    ProFeature *feature,
    ProError	status,
    ProAppData  num_csys)	
{
    ProError err;
    ProFeattype type;

    err = ProFeatureTypeGet(feature, &type);
    TEST_CALL_REPORT("ProFeatureTypeGet()",
		    "ProUtilNonCsysFeatCheck()", err, err != PRO_TK_NO_ERROR);
    if (type == PRO_FEAT_CSYS)
    {
	((int*)num_csys)[0]++;
	return (PRO_TK_NO_ERROR);
    }
    else
    {
	return (PRO_TK_E_FOUND);
    }
}


/*=============================================================*\
  FUNCTION: ProTestDtmplnMenuSet
  PURPOSE:  Allow/Disable menu buttons
\*=============================================================*/
ProError ProTestDtmplnMenuSet(
    DtmPlnConstr *constr)
{
    int i;

    for (i=0; i<MAX_CONSTR_TYPES; i++)
    {
	if (constr_type[i].state_en != (constr->constr_enabled & (1 << i)))
	{
	    constr_type[i].state_en = constr->constr_enabled & (1 << i);
	    ProUtilMenubuttonActivate((char *)"tkdtm plane", constr_type[i].button_name,
		constr_type[i].state_en);
	}
    }

    for (i=0; i<SIZEOFARR(ref_type); i++)
    {
	if ((ref_type[i].state_en != (constr->ref_enabled & (1 << i))) ||
	    (ref_type[i].state_hi != (constr->ref_highlighted & (1 << i))))
	{
	    ref_type[i].state_en = constr->ref_enabled & (1 << i);
	    ref_type[i].state_hi = constr->ref_highlighted & (1 << i);
	    ProUtilMenubuttonActivate((char *)"tkdtmplnc", ref_type[i].button_name, 1);
	    ProUtilMenubuttonHighlight((char *)"tkdtmplnc", ref_type[i].button_name,
		ref_type[i].state_hi);
	    ProUtilMenubuttonActivate((char *)"tkdtmplnc", ref_type[i].button_name,
		ref_type[i].state_en);
	}
    }
    
    return (PRO_TK_NO_ERROR);
}



/*=============================================================*\
  FUNCTION: ProUtilMenucompDelete
  PURPOSE:  ProMenuDelete function for the compound menu
\*=============================================================*/
static ProError ProUtilMenucompDelete()
{
    ProError err;

    err = ProMenuDelete();
    err = ProMenuDelete();
    err = ProMenuDelete();
    TEST_CALL_REPORT("ProMenuDelete()",
			    "ProTestDtmpln()", err, err != PRO_TK_NO_ERROR);
    return (PRO_TK_NO_ERROR);
}

/*=============================================================*\
  FUNCTION: ProTestDtmplnDone
  PURPOSE:  Done creation
\*=============================================================*/
int ProTestDtmplnDone(
    DtmPlnConstr *constr)
{
    ProElement tree, constr_elem, constrs_elem;
    ProFeature feat;
    ProError err;
    ProErrorlist    errs;
    ProModelitem    modelitem;
    ProSelection    featsel;
    ProFeatureCreateOptions *opts = 0;
    int i;

    static ElemTreeData dtmpln[] = {
	{0, PRO_E_FEATURE_TREE, {(ProValueDataType)-1}},
	{1, PRO_E_FEATURE_TYPE, {PRO_VALUE_TYPE_INT, {PRO_FEAT_DATUM}}},
	{1, PRO_E_DTMPLN_CONSTRAINTS, {(ProValueDataType)-1}}
    };
    static ElemTreeData constr_dtmpln[] = {
	{0, PRO_E_DTMPLN_CONSTRAINT, {(ProValueDataType)-1}},
	{1, PRO_E_DTMPLN_CONSTR_TYPE, {PRO_VALUE_TYPE_INT, {PRO_DTMPLN_OFFS}}},
	{1, PRO_E_DTMPLN_CONSTR_REF, {PRO_VALUE_TYPE_SELECTION}}
    };
    static ElemTreeData offcsys_dtmpln[] = {
	{1, PRO_E_DTMPLN_OFF_CSYS, {PRO_VALUE_TYPE_INT}},
	{1, PRO_E_DTMPLN_OFF_CSYS_OFFSET, {PRO_VALUE_TYPE_DOUBLE}}
    };
    static ElemTreeData offs_dtmpln[] = {
	{1, PRO_E_DTMPLN_CONSTR_REF_OFFSET, {PRO_VALUE_TYPE_DOUBLE}}
    };
    static ElemTreeData angle_dtmpln[] = {
	{1, PRO_E_DTMPLN_CONSTR_REF_ANGLE, {PRO_VALUE_TYPE_DOUBLE}}
    };
    static ElemTreeData blend_dtmpln[] = {
	{1, PRO_E_DTMPLN_SEC_IND, {PRO_VALUE_TYPE_INT}}
    };
    static ProElempathItem constr_path[] = {
	{PRO_ELEM_PATH_ITEM_TYPE_ID, PRO_E_DTMPLN_CONSTRAINTS}
    };
        

    err = ProUtilElemtreeCreate(dtmpln, SIZEOFARR(dtmpln), NULL, &tree);

    err = ProUtilElemtreeElementGet(tree, constr_path, 
	SIZEOFARR(constr_path), &constrs_elem);

    for (i=0; i<constr->n_constr; i++)
    {
	constr_dtmpln[1].data.v.i = constr->type[i];
	constr_dtmpln[2].data.v.r = constr->ref[i];
	err = ProUtilElemtreeCreate(constr_dtmpln, SIZEOFARR(constr_dtmpln), 
	    NULL, &constr_elem);
	
	switch(constr->type[i])
	{
	case DCTR_OFFSET:
	    if (constr->index[i] == -1)
	    {
		/* Offset plane */
		offs_dtmpln[0].data.v.d = constr->offset[i];
		err = ProUtilElemtreeCreate(offs_dtmpln, SIZEOFARR(offs_dtmpln), 
		    constr_elem, &constr_elem);
	    }
	    else
	    {
		/* Offset csys */
		offcsys_dtmpln[0].data.v.i = constr->index[i];
		offcsys_dtmpln[1].data.v.d = constr->offset[i];
		err = ProUtilElemtreeCreate(offcsys_dtmpln,
		    SIZEOFARR(offcsys_dtmpln), constr_elem, &constr_elem);
	    }
	    break;
	case DCTR_ANGLE:
	    offs_dtmpln[0].data.v.d = constr->offset[i];
	    err = ProUtilElemtreeCreate(angle_dtmpln, SIZEOFARR(angle_dtmpln), 
		constr_elem, &constr_elem);
	    break;
	case DCTR_BLEND_SECTION:
	    offs_dtmpln[0].data.v.i = constr->index[i];
	    err = ProUtilElemtreeCreate(blend_dtmpln, SIZEOFARR(blend_dtmpln), 
		constr_elem, &constr_elem);
	    break;
    	}


   /* For testing purposes used ProElementArraySet/Get, the better way use 
	ProElemtreeElementAdd */
#if 0
	err = ProArrayAlloc(0, sizeof(ProElement), 1, (ProArray*)&p_elems);
	TEST_CALL_REPORT("ProArrayAlloc()",
			    "ProTestDtmplnDone()", err, err != PRO_TK_NO_ERROR);

	err = ProElementArrayGet(constrs_elem, NULL, &p_elems);
	TEST_CALL_REPORT("ProElementArrayGet()",
			    "ProTestDtmplnDone()", err, err != PRO_TK_NO_ERROR);

	if (err != PRO_TK_NO_ERROR)
	{
	    /* Added since bug in func ProElementArrayGet in the pipe mode */

	    err = ProArrayAlloc(0, sizeof(ProElement), 1, (ProArray*)&p_elems);
	    TEST_CALL_REPORT("ProArrayAlloc()",
			    "ProTestDtmplnDone()", err, err != PRO_TK_NO_ERROR);
	}
	
	err = ProArrayObjectAdd((ProArray*)&p_elems, PRO_VALUE_UNUSED,
	    1, &constr_elem);
	TEST_CALL_REPORT("ProArrayObjectAdd()",
			    "ProTestDtmplnDone()", err, err != PRO_TK_NO_ERROR);
    
	err = ProElementArraySet(constrs_elem, NULL, p_elems);
	TEST_CALL_REPORT("ProElementArraySet()",
			    "ProTestDtmplnDone()", err, err != PRO_TK_NO_ERROR);
    
	err = ProArrayFree((ProArray*)&p_elems);
	TEST_CALL_REPORT("ProArrayFree()",
			    "ProTestDtmplnDone()", err, err != PRO_TK_NO_ERROR);
#else
	err = ProElemtreeElementAdd(constrs_elem, NULL, constr_elem);
	TEST_CALL_REPORT("ProElemtreeElementAdd()",
			    "ProTestDtmplnDone()", err, err != PRO_TK_NO_ERROR);

#endif
    }

/*------------------------------------------------------------------*\
	Alloc selection to the model
\*------------------------------------------------------------------*/
    err = ProMdlToModelitem(constr->model, &modelitem);
    TEST_CALL_REPORT("ProMdlToModelitem()",
			    "ProTestDtmplnDone()", err, err != PRO_TK_NO_ERROR);
    err = ProSelectionAlloc(NULL, &modelitem, &featsel);
    TEST_CALL_REPORT("ProSelectionAlloc()",
			    "ProTestDtmplnDone()", err, err != PRO_TK_NO_ERROR);

/*------------------------------------------------------------------*\
	Create datum plane
\*------------------------------------------------------------------*/
    err = ProUtilElementtreePrint(tree, PRO_TEST_INFO_WINDOW, NULL);

    err = ProArrayAlloc(1,sizeof(ProFeatureCreateOptions),
        1, (ProArray*)&opts);

    opts[0]= PRO_FEAT_CR_NO_OPTS;

    err = ProFeatureWithoptionsCreate(featsel, tree,
        opts, PRO_REGEN_NO_FLAGS, &feat, &errs);    
    TEST_CALL_REPORT("ProFeatureWithoptionsCreate()",
			    "ProTestDtmplnDone()", err, err != PRO_TK_NO_ERROR);

    err = ProArrayFree((ProArray*)&opts);

    if (err != PRO_TK_NO_ERROR)
    {
	ProUtilFeatErrsPrint(&errs);
    }
    else
    {
	ProUtilMsgPrint(msgfil, (char *)"TEST %0s has been created successfully.", 
	    (char *)"DATUM PLANE");
	err = ProTreetoolRefresh(constr->model);
	TEST_CALL_REPORT("ProTreetoolRefresh()",
			    "ProTestDtmplnDone()", err, err != PRO_TK_NO_ERROR);
    }
    err = ProElementFree(&tree);
    TEST_CALL_REPORT("ProElementFree()",
			    "ProTestDtmplnDone()", err, err != PRO_TK_NO_ERROR);
    err = ProSelectionFree(&featsel);
    TEST_CALL_REPORT("ProSelectionFree()",
			    "ProTestDtmplnDone()", err, err != PRO_TK_NO_ERROR);

    ProUtilMenucompDelete(); 
    return (0);
}

/*=============================================================*\
  FUNCTION: ProTestDtmplnRestart
  PURPOSE:  Restart datum plane creation
\*=============================================================*/
int ProTestDtmplnRestart(
    DtmPlnConstr *constr)
{
    int i;
    ProMdl model;

    ProUtilMsgPrint(msgfil, 
	(char *)"TEST Datum plane was restarted. Select options again.");

    if (constr->cur_constr != -1)
	ProUtilMenubuttonHighlight((char *)"tkdtm plane", 
	    constr_type[constr->cur_constr].button_name, 0);

    ProUtilMenubuttonHighlight((char *)"tkdtmplnd", (char *)"Restart", 0);

    model = constr->model;
    memset(constr, '\0', sizeof(DtmPlnConstr));
    constr->cur_constr = -1;
    constr->constr_enabled = -1;
    constr->model = model;

    for (i=0; i<SIZEOFARR(constr_type); i++)
	constr_type[i].state_en = -1;
    for (i=0; i<SIZEOFARR(ref_type); i++)
    {
	ref_type[i].state_en = -1;
	ref_type[i].state_hi = -1;
    }

    ProTestDtmplnMenuSet(constr);
    return (0);
}

/*=============================================================*\
  FUNCTION: ProUtilMenucompRestart
  PURPOSE:  Restart datum plane creation
\*=============================================================*/
int ProTestDtmplnQuit(
    DtmPlnConstr *constr)
{
    ProUtilMsgPrint(msgfil, (char *)"TEST Datum plane was not created.");
    ProUtilMenucompDelete(); 
    return (0);
}

/*=============================================================*\
  FUNCTION: ProUtilOffsetPlaneThruPoint
  PURPOSE:  Calculate offset to datum plane by pick point on the
	    model
\*=============================================================*/
int ProUtilOffsetPlaneThruPoint(
    ProGeomitemdata **p_data,
    double *offset)
{
    ProError err;
    ProPoint3d point;
    ProSelection *p_sel_arr;
    ProGeomitemdata *data =*p_data;
    int n_sel;

    err = ProSelect((char *)"surface,datum", 1, NULL, NULL, NULL, NULL, &p_sel_arr,
	&n_sel);
    TEST_CALL_REPORT("ProSelect()",
		"ProUtilOffsetPlaneThruPoint()", err, err != PRO_TK_NO_ERROR);
    if (err != PRO_TK_NO_ERROR || n_sel != 1)
	return(-1);
    err = ProSelectionPoint3dGet(p_sel_arr[0], point);
    TEST_CALL_REPORT("ProSelectionPoint3dGet()",
		"ProUtilOffsetPlaneThruPoint()", err, err != PRO_TK_NO_ERROR);

    *offset = ProUtilPointPlaneDist(point, 
	data->data.p_surface_data->srf_shape.plane.origin, 
	data->data.p_surface_data->srf_shape.plane.e3); 

    err = ProMenuDeleteWithStatus(0);
    TEST_CALL_REPORT("ProMenuDeleteWithStatus()",
		"ProUtilOffsetPlaneThruPoint()", err, err != PRO_TK_NO_ERROR);
    return (0);
}

/*=============================================================*\
  FUNCTION: ProUtilOffsetPlaneThruPoint
  PURPOSE:  Calculate offset to datum plane by pick point on the
	    model
\*=============================================================*/
int ProUtilOffsetPlaneEnterValue(
    ProGeomitemdata **p_data,
    double *p_offset)
{
    ProGeomitemdata *data =*p_data;
    ProError err;
    double offset;

    *p_offset = 0.0;
    ProUtilMsgPrint(msgfil, 
	(char *)"TEST Enter offset in the indicated direction, <ESC> to quit[%0f]:",
	&offset);

    err = ProMessageDoubleRead(NULL, &offset);
    TEST_CALL_REPORT("ProMessageDoubleRead()",
	    "ProUtilOffsetPlaneEnterValue()", err, err != PRO_TK_NO_ERROR);
     
     if (err != PRO_TK_NO_ERROR && err != PRO_TK_GENERAL_ERROR)
	return (-1);

    *p_offset = offset;

    err = ProMenuDeleteWithStatus(0);
    TEST_CALL_REPORT("ProMenuDeleteWithStatus()",
	    "ProUtilOffsetPlaneEnterValue()", err, err != PRO_TK_NO_ERROR);
    return(0);
}

/*=============================================================*\
  FUNCTION: ProUtilDtmplnSelPostFilter
  PURPOSE:  Post filter for selection
\*=============================================================*/
ProError ProUtilDtmplnSelPostFilter(
    ProSelection sel,
    DtmPlnConstr *constr)
{
    ProError err;
    ProBoolean ok = PRO_B_TRUE;
    ProModelitem modelitem;
    ProGeomitemdata *p_data;
    int num_sec;

/*    TEST_CALL_REPORT("ProSelectionPostFilter()",
					"ProUtilDtmplnSelPostFilter()", 0, 0);*/
    
    err = ProSelectionModelitemGet(sel, &modelitem);
    TEST_CALL_REPORT("ProSelectionModelitemGet()",
		  "ProUtilDtmplnSelPostFilter()", err, err != PRO_TK_NO_ERROR);

    if (modelitem.type == PRO_EDGE || modelitem.type == PRO_SURFACE)
    {
	err = ProGeomitemdataGet((ProGeomitem*)&modelitem, &p_data);
	TEST_CALL_REPORT("ProGeomitemdataGet()",
		  "ProUtilDtmplnSelPostFilter()", err, err != PRO_TK_NO_ERROR);

	if (modelitem.type == PRO_EDGE && 
	    p_data->data.p_curve_data->line.type != PRO_ENT_LINE &&
	    p_data->data.p_curve_data->line.type != PRO_ENT_ARC &&
	    p_data->data.p_curve_data->line.type != PRO_ENT_ELLIPSE)
	    ok = PRO_B_FALSE;

	if (modelitem.type == PRO_SURFACE)
	{
	    if (constr->cur_constr == DCTR_TANGENT &&
		p_data->data.p_surface_data->type != PRO_SRF_CYL &&
		p_data->data.p_surface_data->type != PRO_SRF_CONE)
		ok = PRO_B_FALSE;
	    if ((constr->cur_constr == DCTR_PARALLEL ||
		 constr->cur_constr == DCTR_NORMAL ||
		 constr->cur_constr == DCTR_OFFSET ||
		 constr->cur_constr == DCTR_ANGLE) &&
		p_data->data.p_surface_data->type != PRO_SRF_PLANE)
		ok = PRO_B_FALSE;
	    if (constr->cur_constr == DCTR_THROUGH &&
		p_data->data.p_surface_data->type != PRO_SRF_PLANE &&
		p_data->data.p_surface_data->type != PRO_SRF_CYL &&
		p_data->data.p_surface_data->type != PRO_SRF_CONE)
		ok = PRO_B_FALSE;
	}

	err = ProGeomitemdataFree(&p_data);
	TEST_CALL_REPORT("ProGeomitemdataFree()",
		  "ProUtilDtmplnSelPostFilter()", err, err != PRO_TK_NO_ERROR);
    }

    if (modelitem.type == PRO_FEATURE)
    {
	err = ProFeatureNumSectionsGet((ProFeature*)&modelitem, &num_sec);
	TEST_CALL_REPORT("ProFeatureNumSectionsGet()",
		  "ProUtilDtmplnSelPostFilter()", err, err != PRO_TK_NO_ERROR);

	if (err != PRO_TK_NO_ERROR || num_sec<1)
	    ok = PRO_B_FALSE;
    }

    return (ok ? PRO_TK_NO_ERROR : PRO_TK_GENERAL_ERROR);
} 

/*=============================================================*\
  FUNCTION: ProUtilDtmplnSelPostSelact
  PURPOSE:  Post action function for selection
\*=============================================================*/
ProError ProUtilDtmplnSelPostSelact(
    Pro3dPnt pnt,
    ProSelection sel,
    DtmPlnConstr *constr)
{
    ProError err;
    ProModelitem modelitem;
    double offset;
    int action;
    char *c= (char *)"x";

/*    TEST_CALL_REPORT("ProSelectionPostSelact()",
					"ProUtilDtmplnSelPostSelact()", 0, 0);*/

    if (constr->cur_constr == DCTR_OFFSET )
    {
	err = ProSelectionModelitemGet(sel, &modelitem);
	TEST_CALL_REPORT("ProSelectionModelitemGet()",
		"ProUtilDtmplnSelPostSelact()", err, err != PRO_TK_NO_ERROR);
	if (modelitem.type == PRO_CSYS)
	{
	    err = ProMenuFileRegister((char *)"tkoff csys", (char *)"tkoffcsys.mnu", NULL);
	    err = ProMenubuttonActionSet((char *)"tkoff csys", (char *)"X Axis",
		(ProMenubuttonAction)ProUtilMenubuttonDeleteWithStatus, NULL, PRO_DTMPLN_OFF_CSYS_X);
	    err = ProMenubuttonActionSet((char *)"tkoff csys", (char *)"Y Axis",
		(ProMenubuttonAction)ProUtilMenubuttonDeleteWithStatus, NULL, PRO_DTMPLN_OFF_CSYS_Y);
	    err = ProMenubuttonActionSet((char *)"tkoff csys", (char *)"Z Axis",
		(ProMenubuttonAction)ProUtilMenubuttonDeleteWithStatus, NULL, PRO_DTMPLN_OFF_CSYS_Z);
	    err = ProMenubuttonActionSet((char *)"tkoff csys", (char *)"tkoff csys",
		(ProMenubuttonAction)ProMenubuttonDelete, NULL, 0);

	    err = ProMenuCreate(PROMENUTYPE_MAIN, (char *)"tkoff csys", NULL);
	    err = ProMenuProcess((char *)"tkoff csys", &action);

	    if (err == PRO_TK_NO_ERROR)
	    {
    		constr->index[constr->n_constr] = action;
    		offset = 0.0;
		
		c[0] = action - PRO_DTMPLN_OFF_CSYS_X + 'x';
		ProUtilMsgPrint(msgfil, 
		    (char *)"TEST Enter offset value in the %0s-direction [%1f]:",
		     c, &offset);
		err = ProMessageDoubleRead(NULL, &offset);
		TEST_CALL_REPORT("ProMessageDoubleRead()",
		    "ProUtilDtmplnSelPostSelact()", err, err !=PRO_TK_NO_ERROR);
		constr->offset[constr->n_constr] = offset;
	    }
	}
    }
    return (PRO_TK_NO_ERROR);
}

/*=============================================================*\
  FUNCTION: ProTestDtmplnConstrAdd
  PURPOSE:  Add new constraint
\*=============================================================*/
int ProTestDtmplnConstrAdd(
    DtmPlnConstr *constr,	/* In: Setup struct */
    int index)			/* In : Const index in the table */
{
    ProCharLine line;
    int i, n_sel, action, reftype;
    ProSelection *p_sel_arr;
    ProModelitem modelitem;
    ProGeomitemdata *p_data;
    ProSelFunctions func;
    ProError err;
    double offset = 0.0;

    if (constr->ref_pressed)
	constr->ref_pressed = PRO_B_FALSE;
    else
    {
	constr->cur_constr = index;

	for (i=0, constr->ref_enabled = 0; i<SIZEOFARR(ref_type); i++)
	    if (ref_type[i].freedom[index] > 0)
		constr->ref_enabled |= (1 << i);
	constr->ref_highlighted = constr->ref_enabled;
    }
    ProTestDtmplnMenuSet(constr);

    if (constr->cur_constr == DCTR_BLEND_SECTION)
    {
    	ProUtilstrcpy(line, "feature");
	ProUtilMsgPrint(msgfil, (char *)"TEST Not implemented.");
	return (0);
    }
    else
	for (i=0, line[0] = '\0'; i<SIZEOFARR(ref_type); i++)
	{
	    if (constr->ref_highlighted & (1 << i))
	    {
		if (line[0] != '\0')
		    ProUtilstrcat(line, ",");
		ProUtilstrcat(line, (const char*)ref_type[i].select_option);
	    }
	}

    memset(&func, '\0', sizeof(func));
    func.post_filter = (ProSelectionPostFilter)ProUtilDtmplnSelPostFilter;
    func.post_selact = (ProSelectionPostSelact)ProUtilDtmplnSelPostSelact;
    func.app_data = (ProAppData)constr;
    /*  do
    {*/
	err = ProSelect(line, 1, NULL, &func, NULL, NULL, &p_sel_arr, &n_sel);
	TEST_CALL_REPORT("ProSelect()",
    	    "ProTestDtmplnConstrAdd()", err, err != PRO_TK_NO_ERROR &&
	    err != PRO_TK_PICK_ABOVE);

	/* } while(err != PRO_TK_PICK_ABOVE && !(err ==PRO_TK_NO_ERROR && n_sel == 1));
	 */
    if (err != PRO_TK_NO_ERROR)
	return (0);

    err = ProSelectionModelitemGet(p_sel_arr[0], &modelitem);
    TEST_CALL_REPORT("ProSelectionModelitemGet()",
		    "ProTestDtmplnConstrAdd()", err, err != PRO_TK_NO_ERROR);

    constr->ref_enabled = 0;
    constr->ref_highlighted = 0;
    ProUtilMenubuttonHighlight((char *)"tkdtm plane", constr_type[index].button_name, 
	0);
    ProTestDtmplnMenuSet(constr);

    err = ProGeomitemdataGet((ProGeomitem*)&modelitem, &p_data);
    TEST_CALL_REPORT("ProGeomitemdataGet()",
		    "ProTestDtmplnConstrAdd()", err, err != PRO_TK_NO_ERROR);

    err = ProSelectionCopy(p_sel_arr[0], &constr->ref[constr->n_constr]);
    TEST_CALL_REPORT("ProSelectionCopy()",
		    "ProTestDtmplnConstrAdd()", err, err != PRO_TK_NO_ERROR);

/*------------------------------------------------------------*\
    Add constarint
\*------------------------------------------------------------*/
    if (index == DCTR_OFFSET)
    {
	if (modelitem.type == PRO_SURFACE)
	{
	    err = ProMenuFileRegister((char *)"tkoffset", (char *)"tkoffset.mnu", NULL);
	    err = ProMenubuttonGenactionSet((char *)"tkoffset", (char *)"Thru point",
		(ProMenubuttonGenaction)ProUtilOffsetPlaneThruPoint, &p_data,
		&offset, NULL, NULL, NULL, NULL);
	    err = ProMenubuttonGenactionSet((char *)"tkoffset", (char *)"Enter Value",
		(ProMenubuttonGenaction)ProUtilOffsetPlaneEnterValue, &p_data,
		&offset, NULL, NULL, NULL, NULL);
	    err = ProMenubuttonActionSet((char *)"tkoffset", (char *)"tkoffset",
		(ProMenubuttonAction)ProMenuDelete, NULL, 0);

	    err = ProMenuCreate(PROMENUTYPE_MAIN, (char *)"tkoffset", NULL);
	    err = ProMenuCommandPush((char *)"Thru point");
	    TEST_CALL_REPORT("ProMenuCommandPush()",
		    (char *)"ProTestDtmplnConstrAdd()", err, err != PRO_TK_NO_ERROR);
	    
	    err = ProMenuProcess((char *)"tkoffset", &action);
	    if (err != PRO_TK_NO_ERROR)
		return (0);

	    constr->offset[constr->n_constr] = offset;
	    constr->index[constr->n_constr] = -1;
	}
    }
    if (index == DCTR_ANGLE)
    {
	ProUtilMsgPrint(msgfil, (char *)"TEST Angle in indicated direction, [%0f]:",
	    &offset);
	err = ProMessageDoubleRead(NULL, &offset);
	TEST_CALL_REPORT("ProMessageDoubleRead()",
		    "ProTestDtmplnConstrAdd()", err, err != PRO_TK_NO_ERROR);
	constr->offset[constr->n_constr] = offset;
    }

    /* Here constraint should be checked */
    switch (modelitem.type)
    {
	case PRO_AXIS:
	case PRO_EDGE:
	case PRO_CURVE:
	    if (p_data->data.p_curve_data->line.type == PRO_ENT_LINE)
		reftype = USER_LINE;
	    else
		reftype = USER_PLANE;
	    break;
	case PRO_POINT:
	case PRO_EDGE_START:
	case PRO_EDGE_END:
	case PRO_CRV_START:
	case PRO_CRV_END:
	    reftype = USER_POINT;
	    break;
	case PRO_SURFACE:
	    if (p_data->data.p_surface_data->type == PRO_SRF_PLANE)
		reftype = USER_PLANE;
	    else
		reftype = USER_CYL;
	    break;
	case PRO_CSYS:
		reftype = USER_CSYS;
	    break;
	case PRO_FEATURE:
	    reftype = USER_PLANE;
	    index = DCTR_THROUGH;
	    /* Here we must find the feature section */
    	    break;
     }

    err = ProGeomitemdataFree(&p_data);
    TEST_CALL_REPORT("ProGeomitemdataFree()",
		    "ProTestDtmplnConstrAdd()", err, err != PRO_TK_NO_ERROR);

    constr->type[constr->n_constr] = (ProDtmplnConstrType)constr->cur_constr;

    constr->n_constr++;
    constr->freedom += ref_type[reftype].freedom[index];
    if (index == DCTR_THROUGH || index == DCTR_OFFSET)
	constr->through = PRO_B_TRUE;

    /* Allow offset and Blend be only first constraint */
    constr->constr_enabled = constr->constr_enabled & 
	(~((1 << DCTR_OFFSET)|(1 << DCTR_BLEND_SECTION)));
    if (constr->cur_constr == DCTR_PARALLEL)
	constr->constr_enabled = 
	    constr->constr_enabled & (~(1 << DCTR_PARALLEL));

    if ((constr->freedom >= 3) && constr->through)
    {
	ProUtilMsgPrint(msgfil, (char *)"TEST Datum Plane is fully constrained.");
	constr->constr_enabled = 0;
    }	 

    ProTestDtmplnMenuSet(constr);

    return (0);
}

/*=============================================================*\
  FUNCTION: ProTestDtmplnConstrRef
  PURPOSE:  Enable/Disable constraint ref
\*=============================================================*/
int ProTestDtmplnConstrRef(
    DtmPlnConstr *constr,
    int index)
{
    ProError err;

    constr->ref_pressed = PRO_B_TRUE;
    constr->ref_highlighted ^= (1 << index);

    err = ProMenuCommandPush(constr_type[constr->cur_constr].button_name);
    TEST_CALL_REPORT("ProMenuCommandPush()",
		    "ProTestDtmplnConstrRef()", err, err != PRO_TK_NO_ERROR);
    return (0);
}

/*=============================================================*\
  FUNCTION: ProTestDtmplnCreate
  PURPOSE:  Create datum plane feature
\*=============================================================*/
int ProTestDtmplnCreate(
    ProMdl model)
{
    ProError err;
    int action, i, n_menus;
    char *compound[] = {(char *)"tkdtm plane", (char *)"tkdtmplnc", (char *)"tkdtmplnd", (char *)""};

    memset(&constraint, '\0', sizeof(constraint));
    constraint.cur_constr = -1;
    constraint.constr_enabled = -1;
    constraint.model = model;

    err = ProMenuFileRegister((char *)"tkdtm plane", (char *)"tkdtmpln.mnu", NULL);
    for (i=0; i<SIZEOFARR(constr_type); i++)
    {
	err = ProMenubuttonActionSet((char *)"tkdtm plane", constr_type[i].button_name,
	    (ProMenubuttonAction)ProTestDtmplnConstrAdd, &constraint, i);
	constr_type[i].state_en = -1;
    }
    err = ProMenubuttonActionSet((char *)"tkdtm plane", (char *)"tkdtm plane",
	(ProMenubuttonAction)ProTestDtmplnQuit, NULL, 0);

    err = ProMenuFileRegister((char *)"tkdtmplnc", (char *)"tkdtmplnc.mnu", NULL);
    for (i=0; i<SIZEOFARR(ref_type); i++)
    {
	err = ProMenubuttonActionSet((char *)"tkdtmplnc", ref_type[i].button_name, 
	    (ProMenubuttonAction)ProTestDtmplnConstrRef, &constraint, i);

	ref_type[i].state_en = -1;
	ref_type[i].state_hi = -1;
    }

    err = ProMenuFileRegister((char *)"tkdtmplnd",(char *)"tkdtmplnd.mnu", NULL);
    err = ProMenubuttonActionSet((char *)"tkdtmplnd", (char *)"Done", 
	(ProMenubuttonAction)ProTestDtmplnDone, &constraint, 0);
    err = ProMenubuttonActionSet((char *)"tkdtmplnd", (char *)"Restart",
	(ProMenubuttonAction)ProTestDtmplnRestart, &constraint, 0);
    err = ProMenubuttonActionSet((char *)"tkdtmplnd", (char *)"Quit",
	(ProMenubuttonAction)ProTestDtmplnQuit, &constraint, 0);

    err = ProMenuPush();
    TEST_CALL_REPORT("ProMenuPush()",
			    "ProTestDtmpln()", err, err != PRO_TK_NO_ERROR);

    err = ProMenuDatamodeSet((char *)"tkdtmplnc", PRO_B_TRUE);
    TEST_CALL_REPORT("ProMenuDatamodeSet()",
			    "ProTestDtmpln()", err, err != PRO_TK_NO_ERROR);

    err = ProCompoundmenuCreate(compound, &n_menus);
    TEST_CALL_REPORT("ProCompoundmenuCreate()",
			    "ProTestDtmpln()", err, err != PRO_TK_NO_ERROR);

    ProTestDtmplnMenuSet(&constraint);
    err = ProMenuProcess((char *)"tkdtm plane", &action);
    err = ProMenuPop();
    TEST_CALL_REPORT("ProMenuPop()",
			    "ProTestDtmpln()", err, err != PRO_TK_NO_ERROR);

    return (PRO_TK_NO_ERROR);
}

/*=============================================================*\
  FUNCTION: ProTestDtmpln
  PURPOSE:  Create datum plane feature main funct
\*=============================================================*/
ProError ProTestDtmpln()
{
    ProMdl   model;
    ProError err;
    int	     n_csys = 0;

    err = ProMdlCurrentGet(&model);
    TEST_CALL_REPORT("ProMdlCurrentGet()",
			    "ProTestDtmpln()", err, err != PRO_TK_NO_ERROR);

    err = ProSolidFeatVisit((ProSolid)model, 
	(ProFeatureVisitAction)ProUtilNonCsysFeatCheck, 
	(ProFeatureFilterAction)NULL, (ProAppData)&n_csys);
    TEST_CALL_REPORT("ProSolidFeatVisit()",
	"ProTestDtmpln()", err, err != PRO_TK_NO_ERROR && 
	err != PRO_TK_E_FOUND);

    if (err == PRO_TK_E_FOUND)
    {
	ProTestDtmplnCreate(model);
    }
    else
    {
	ProTestDtmplnDefCreate(model, n_csys);
    }
    return (PRO_TK_NO_ERROR);
}