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

#include <ProToolkit.h>
#include <ProAsmcomp.h>
#include <ProAsmcomppath.h>
#include <ProFeatType.h>
#include <ProMdl.h>
#include <ProSolid.h>
#include <ProSelection.h>

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


/*====================================================================*\
    FUNCTION :	ProUtilCollectOneLevelAsmcomppath()
    PURPOSE  :	Collect Asmcomppathes on one level only.
		Util function to be call from ProUtilCollectAsmcomppath
    NOTE     :  Do not call it directly
\*====================================================================*/
static ProError ProUtilCollectOneLevelAsmcomppath(
    ProAssembly assy,
    ProAsmcomppath *p_path,
    int level,
    ProAsmcomppath **pp_path)
{
    ProError err;
    ProAsmcomp *p_comps;
    int i, n_comps;
    ProMdl mdl;
    ProMdlType mdl_type;

    err = ProUtilCollectAsmcomp(assy, &p_comps);
    if (err != PRO_TK_NO_ERROR)
	return (err);

    err = ProArraySizeGet((ProArray)p_comps, &n_comps);
    for (i=0; i<n_comps; i++)
    {
	/* Fill full Asmcomppath structure for component */ 
	p_path->comp_id_table[level] = p_comps[i].id;
	p_path->comp_id_table[level + 1] = -1;
	p_path->table_num = level + 1;

	/* Add Asmcomppath to the array */
	ProArrayObjectAdd((ProArray*)pp_path, PRO_VALUE_UNUSED, 1, p_path);

	/* Check if components is assembly for recursive checking */
	err = ProAsmcompMdlGet(p_comps+i, &mdl);
	err = ProMdlTypeGet(mdl, &mdl_type);
	if (mdl_type == PRO_MDL_ASSEMBLY)
	{
	    err = ProUtilCollectOneLevelAsmcomppath((ProAssembly)mdl, p_path, 
		level+1, pp_path);
	}
    }
    err = ProArrayFree((ProArray*)&p_comps);
    return (PRO_TK_NO_ERROR);
}

/*====================================================================*\
    FUNCTION :	ProUtilCollectAsmcomppath()
    PURPOSE  :	Collect asmcomppathes for all levels of assembly,
		including parts and subassemblies
\*====================================================================*/
ProError ProUtilCollectAsmcomppath(
    ProAssembly assy,		/* In : Assembly */
    ProAsmcomppath **pp_path,	/* Out: The ProArray of collected pathes.
					The function allocates memory for 
					this argument, but you must free it. 
					To free the memory, call the function 
					ProArrayFree() */
    int *p_n_pathes)		/* Out: The number of pathes in the array */
{
    ProError err;
    ProAsmcomppath path;
    int n_pathes;
    
    if((assy == (ProAssembly)NULL) || pp_path == NULL )
	return (PRO_TK_BAD_INPUTS);

    err = ProArrayAlloc(0, sizeof(ProAsmcomppath), 10, (ProArray*)pp_path);
    path.owner = (ProSolid)assy;
    
    err = ProUtilCollectOneLevelAsmcomppath(assy, &path, 0, pp_path);
    err = ProArraySizeGet((ProArray)*pp_path, &n_pathes);
    if (n_pathes<=0)
    {	
	err = ProArrayFree((ProArray*)pp_path);
	return (PRO_TK_E_NOT_FOUND);
    }

    p_n_pathes[0] = n_pathes;
    return (PRO_TK_NO_ERROR);
}


/*====================================================================*\
    FUNCTION :	ProUtilCollectOneLevelDtmPnts()
    PURPOSE  :	Collect Datum points in top level solid only.
		Util function to be call from ProUtilCollectSolidDtmPnts
    NOTE     :  Do not call it directly
\*====================================================================*/
static ProError ProUtilCollectOneLevelDtmPnts(
    ProSolid solid,
    ProAsmcomppath *p_path,
    ProSelection    **p_sel)
{
    ProError err;
    ProFeature *p_features;
    ProModelitem *p_points;
    int i, j, n_points, n_feats;
    ProSelection sel;

    err = ProUtilCollectSolidFeaturesWithFilter (solid, 
        (ProFeatureFilterAction)ProUtilCollectDtmPointFeatFilter, &p_features);

    if (err != PRO_TK_NO_ERROR)
	return (err);
    err = ProArraySizeGet((ProArray)p_features, &n_feats);

    for (i=0; i<n_feats; i++)
    {
	err = ProUtilCollectFeatureGeomitems(p_features + i, PRO_POINT,
	    (ProGeomitem**)&p_points);
	if (err != PRO_TK_NO_ERROR)
	    continue;
	err = ProArraySizeGet((ProArray)p_points, &n_points);
	for (j=0; j<n_points; j++)
	{
	    err = ProSelectionAlloc(p_path, p_points+j, &sel);
	    err = ProArrayObjectAdd((ProArray*)p_sel, PRO_VALUE_UNUSED, 1,
		&sel); 
	}
	err = ProArrayFree((ProArray*)&p_points);
    }
    err = ProArrayFree((ProArray*)&p_features);
    return (PRO_TK_NO_ERROR);
}

/*====================================================================*\
    FUNCTION :  ProUtilCollectSolidDtmPnts()
    PURPOSE  :  Collect solid's  datum points. The solid can be part or
                assembly. In case of assebly all level points will be
                collected
\*====================================================================*/
ProError ProUtilCollectSolidDtmPnts(
    ProSolid solid,	    /* In : solid */
    ProSelection **pp_sel,  /* Out: ProArray of Selection. The function 
				    allocates memory for this argument, but 
				    you must free it. To free the memory, 
				    call the function 
				    ProUtilCollectionArrayFree() */
    int *p_n_pnts)	    /* Out: The number of points in the array */ 
{
    ProError err;
    ProMdlType mdl_type;
    ProAsmcomppath *p_pathes;
    int i, n_pathes, n_sel;

    if ((solid == (ProSolid)NULL) || (pp_sel == NULL))
	return (PRO_TK_BAD_INPUTS);

    err = ProArrayAlloc(0, sizeof(ProSelection), 10, (ProArray*)pp_sel);

    /* First collect Datum points in the model */
    err = ProUtilCollectOneLevelDtmPnts(solid, NULL, pp_sel);

    /* Check the model type */
    err = ProMdlTypeGet((ProMdl)solid, &mdl_type);
    if (mdl_type == PRO_MDL_ASSEMBLY)
    {
	err = ProUtilCollectAsmcomppath((ProAssembly)solid, &p_pathes,
	    &n_pathes);
	if (err == PRO_TK_NO_ERROR)
	{
	    for (i=0; i<n_pathes; i++)
	    {
		err = ProAsmcomppathMdlGet(p_pathes + i, (ProMdl*)&solid);
		err = ProUtilCollectOneLevelDtmPnts(solid, p_pathes+i, pp_sel);
	    }
	    err = ProArrayFree((ProArray*)&p_pathes);
	}
    }
    err = ProArraySizeGet((ProArray)*pp_sel, &n_sel);
    if (n_sel <=0)
    {
	err  = ProArrayFree((ProArray*)pp_sel);
	return (PRO_TK_E_NOT_FOUND);
    }
    
    p_n_pnts[0] = n_sel;
    return (PRO_TK_NO_ERROR);
}


/*====================================================================*\
    FUNCTION :	ProUtilSelectionArrayFree()
    PURPOSE  :	Free ProArray of ProSelection allocated by collection 
\*====================================================================*/
ProError ProUtilSelectionArrayFree(
    ProSelection **pp_sel)  /* In:ProArray  to free memory */
{
    ProError err;
    int i, n_sel;

    err = ProArraySizeGet((ProArray)*pp_sel, &n_sel);
    for (i=0; i<n_sel; i++)
	err = ProSelectionFree(pp_sel[0]+i);
    err = ProArrayFree((ProArray*)pp_sel);
    pp_sel[0] = (ProSelection*)NULL;
    return (PRO_TK_NO_ERROR);
}