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


/*--------------------------------------------------------------------*\
Pro/Toolkit  includes
\*--------------------------------------------------------------------*/
#include <ProToolkit.h>
#include <ProObjects.h>
#include <ProColor.h>
#include <ProMdl.h>
#include <ProPart.h>
#include <ProMenu.h>
#include <ProGtol.h>
#include <ProGraphic.h>
#include <ProFeature.h>
#include <ProSurface.h>
#include <ProWstring.h>
#include <ProUtil.h>
#include <ProModelitem.h>
#include <ProMessage.h>

#include "TestError.h"
#include "UtilMenu.h"
#include "UtilMessage.h"
#include "UtilString.h"
#include "UtilMath.h"
#include "UtilTypes.h"
#include "PTApplsUnicodeUtils.h"


#define EXPORT_MN_SETUP     01
#define EXPORT_MN_WIREFRAME 11
#define EXPORT_MN_RENDER    12
#define EXPORT_MN_DUMP      13
#define EXPORT_MN_ANGLE     21
#define EXPORT_MN_CHORD     22
#define EXPORT_MN_QUILT     23
#define EXPORT_MN_OPT       24
#define EXPORT_MN_ASSM      33
#define EXPORT_MN_SEL_PART  34

typedef struct pro_test_export_data
{
    ProMdl      mdl;
    double      chord_ht;
    double      angle_cntrl;
    ProBoolean  include_quilts;
    ProCharLine file_name;
    int         file_format;
    ProBoolean  optimization;
} ProTestExportData;

/*=================================================================*\
    FUNCTION : ProTestExportVertexIsEq()
    PURPOSE  : Compare two vectors.
\*=================================================================*/
int ProTestExportVertexIsEq(ProVector v1, ProVector v2)
{
    if((v1 == NULL)||(v2 == NULL))
        return(-1);
        
    if((fabs(v1[0]-v2[0])<EPSM6)&&(fabs(v1[1]-v2[1])<EPSM6)&&(fabs(v1[2]-v2[2])<EPSM6))
        return(1); 
        
    return(0);
}

/*=================================================================*\
    FUNCTION : ProTestExportFindVertex()
    PURPOSE  : Find vertex position in the array.
\*=================================================================*/
int ProTestExportFindVertex(ProVector *v_arr, ProVector v)
{   
    ProError error;
    int i, n;

    if((v_arr == NULL)||(v == NULL))
        return(-1);
        
    error = ProArraySizeGet((ProArray)v_arr, &n);
    TEST_CALL_REPORT ("ProArraySizeGet()",
            "ProTestExportFindVertex()", error, error != PRO_TK_NO_ERROR);
    
    if((error!=PRO_TK_NO_ERROR)||(n<=0))     
        return(-1);
        
    for(i=(n-1); i<=0; i--)
    {
       if (ProTestExportVertexIsEq(v_arr[i], v) == 1)
            return(i);
    }    
    
    return(-1);        
}

/*=================================================================*\
    FUNCTION : ProTestExportVertexAdd()
    PURPOSE  : Add vertex to the array
\*=================================================================*/
int ProTestExportVertexAdd(ProVector **v_arr, ProVector v, ProBoolean opt)
{
    int n;
    ProError error;

    if(v == NULL)
        return(-1);
    
    if(*v_arr == NULL)
    {
        error = ProArrayAlloc(0, sizeof(ProVector),1,(ProArray*)v_arr);
        TEST_CALL_REPORT ("ProArrayAlloc()",
            "ProTestExportVertexAdd()", error, error != PRO_TK_NO_ERROR);
        
        if(error!=PRO_TK_NO_ERROR)
            return(-1);
    }
    
    if(opt == PRO_B_TRUE)
    {
        n = ProTestExportFindVertex(*v_arr, v);
        if(n>=0)
            return(n);    
    }        

    error = ProArrayObjectAdd((ProArray*)v_arr, PRO_VALUE_UNUSED, 1, &v);
    TEST_CALL_REPORT ("ProArrayObjectAdd()",
        "ProTestExportVertexAdd()", error, error != PRO_TK_NO_ERROR);
    
    if(error!=PRO_TK_NO_ERROR)
        return(-1);
    
    error = ProArraySizeGet((ProArray)*v_arr, &n);
    TEST_CALL_REPORT ("ProArraySizeGet()",
        "ProTestExportVertexAdd()", error, error != PRO_TK_NO_ERROR);
        
    if(error!=PRO_TK_NO_ERROR)
        return(-1);
        
    (*v_arr)[n-1][0] =v[0];
    (*v_arr)[n-1][1] =v[1];
    (*v_arr)[n-1][2] =v[2];

    return(n-1);            
}

/*=================================================================*\
    FUNCTION : ProTestExportCollectAllTringles()
    PURPOSE  : Collect all triangles into the single array
\*=================================================================*/
ProError ProTestExportCollectAllTringles(ProSurfaceTessellationData *data, 
                                         ProTriangle **trn, 
                                         ProVector **ver, 
                                         ProBoolean opt)
{
    ProError error, err = PRO_TK_NO_ERROR;
    int i, n_trn = 0, k, j;
    int SurfCount=0;
    
    if(data == NULL)
        return(PRO_TK_BAD_INPUTS);

    if(*trn == NULL)
    {
        error = ProArrayAlloc(0, sizeof(ProTriangle),1,(ProArray*)trn);
        TEST_CALL_REPORT ("ProArrayAlloc()",
            "ProTestExportCollectAllTringles()", error, error != PRO_TK_NO_ERROR);
        
        if(error!=PRO_TK_NO_ERROR)
            return(error);
    }
    
    error = ProArraySizeGet((ProArray)data, &SurfCount);
    TEST_CALL_REPORT ("ProArraySizeGet()",
            "ProTestExportCollectAllTringles()", error, error != PRO_TK_NO_ERROR);
    
    if((error != PRO_TK_NO_ERROR)||(SurfCount<=0))
        return(PRO_TK_BAD_INPUTS);

    for(i=0; i<SurfCount; i++)
    {   
        for(j=0; j<data[i].n_facets; j++)
        {
            error = ProArrayObjectAdd((ProArray*)trn,PRO_VALUE_UNUSED,1, &data[i].facets[j]);
            TEST_CALL_REPORT ("ProArrayObjectAdd()",
                "ProTestExportCollectAllTringles()", error, error != PRO_TK_NO_ERROR);
            
            if(error!=PRO_TK_NO_ERROR)
            {
                err = error;
                continue;
            }    
                
            for(k=0; k<3; k++)
                (*trn)[n_trn][k] = ProTestExportVertexAdd(ver,
                    data[i].vertices[data[i].facets[j][k]], opt);
            n_trn++;
        }        
    }    
    
    return(err);
}

/*=================================================================*\
    FUNCTION : ProTestExportToRender()
    PURPOSE  : Print tessllation data to SLP file
\*=================================================================*/
ProError ProTestExportToRender(FILE *fp, ProSurfaceTessellationData *data, char *name)
{
    ProError error;
    int n,i,j,k;
    
    error = ProArraySizeGet((ProArray)data, &n);
    if((error!=PRO_TK_NO_ERROR)||(n<=0))
        return(PRO_TK_BAD_INPUTS);
    
    ProTKFprintf(fp,"solid %s\n", name);   
/*-----------------------------------------------------------------*\
    Print all facets with gray color
\*-----------------------------------------------------------------*/
    ProTKFprintf(fp,"\tcolor 0.8 0.8 0.8\n");
    for(i=0; i<n; i++)
    {
        for(j=0; j<data[i].n_facets; j++)
        {
            ProTKFprintf(fp,"\tfacet\n");
            for(k=0; k<3; k++)
            {
                ProTKFprintf(fp,"\t\t\tnormal %5.5f %5.5f %5.5f\n", 
                    data[i].normals[data[i].facets[j][k]][0],
                    data[i].normals[data[i].facets[j][k]][1],
                    data[i].normals[data[i].facets[j][k]][2]);
            }                
                
            ProTKFprintf(fp,"\t\touter loop\n");
            for(k=0; k<3; k++)
            {
                ProTKFprintf(fp,"\t\t\tvertex %5.5f %5.5f %5.5f\n", 
                    data[i].vertices[data[i].facets[j][k]][0],
                    data[i].vertices[data[i].facets[j][k]][1],
                    data[i].vertices[data[i].facets[j][k]][2]);
            }                
            ProTKFprintf(fp,"\t\tendloop\n");
            ProTKFprintf(fp,"\tendfacet\n");
        }
    }        
    ProTKFprintf(fp,"endsolid %s",name);
    
    return(error);
}

/*=================================================================*\
    FUNCTION : ProTestExportToDUMP()
    PURPOSE  : DUMP tessllation data to file
\*=================================================================*/
ProError ProTestExportToDUMP(FILE *fp, ProSurfaceTessellationData *data)
{
    ProError error;
    int i, j, SurfCount =0, SurfId;
    ProSrftype SurfType;
    ProCharLine c_name;
    double SurfArea;

/*-----------------------------------------------------------------*\
    Get surface count
\*-----------------------------------------------------------------*/
    error = ProArraySizeGet((ProArray)data, &SurfCount); 
    TEST_CALL_REPORT ("ProArraySizeGet()",
        "ProTestExportToDUMP()", error, error != PRO_TK_NO_ERROR);
    
    if((error != PRO_TK_NO_ERROR)||(SurfCount<=0))
        return(PRO_TK_E_NOT_FOUND);
        
/*-----------------------------------------------------------------*\
    Print file header
\*-----------------------------------------------------------------*/
    ProTKFprintf(fp, "#Surface tessellation data\n");
    ProTKFprintf(fp, "#Version 1.0\n\n");
    ProTKFprintf(fp, "Surface count: %d\n",SurfCount);
    for(i=0; i<SurfCount;i++)
    {
/*-----------------------------------------------------------------*\
        Print information about current surface
\*-----------------------------------------------------------------*/
        ProTKFprintf(fp, "Surface_%d {\n",i);
        error = ProSurfaceIdGet(data[i].surface, &SurfId);
        TEST_CALL_REPORT ("ProSurfaceIdGet()",
            "ProTestExportToDUMP()", error, error != PRO_TK_NO_ERROR);
        
        ProTKFprintf(fp,"\tid: %d\n", SurfId);
        
        error = ProSurfaceTypeGet(data[i].surface, &SurfType);
        TEST_CALL_REPORT ("ProSurfaceTypeGet()",
            "ProTestExportToDUMP()", error, error != PRO_TK_NO_ERROR);
        
        ProUtilSrftypeStr(SurfType,c_name);
        ProTKFprintf(fp,"\ttype: (%d) %s\n", SurfType, c_name);
        
        error = ProSurfaceAreaEval(data[i].surface, &SurfArea); 
        TEST_CALL_REPORT ("ProSurfaceAreaEval()",
            "ProTestExportToDUMP()", error, error != PRO_TK_NO_ERROR);
        
        ProTKFprintf(fp,"\tarea: %5.5f\n", SurfArea);
        ProTKFprintf(fp,"\tvertices count: %d\n", data[i].n_vertices);
        ProTKFprintf(fp,"\tfacets count: %d\n", data[i].n_facets);
        
/*-----------------------------------------------------------------*\
        Print array of vertices
\*-----------------------------------------------------------------*/
        ProTKFprintf(fp,"\tVertices {\n");
        for(j=0; j<data[i].n_vertices; j++)
        {
            ProTKFprintf(fp,"\t\t %5.5f, %5.5f, %5.5f\n",
                data[i].vertices[j][0], data[i].vertices[j][1], data[i].vertices[j][2]); 
        }
        ProTKFprintf(fp,"\t}\n");
        
/*-----------------------------------------------------------------*\
        Print array of normals
\*-----------------------------------------------------------------*/
        ProTKFprintf(fp,"\tNormals {\n");
        for(j=0; j<data[i].n_vertices; j++)
        {
            ProTKFprintf(fp,"\t\t %5.5f, %5.5f, %5.5f\n",
                data[i].normals[j][0], data[i].normals[j][1], data[i].normals[j][2]); 
        }
        ProTKFprintf(fp,"\t}\n");
        
/*-----------------------------------------------------------------*\
        Print array of facets
\*-----------------------------------------------------------------*/
        ProTKFprintf(fp,"\tFacets {\n");
        for(j=0; j<data[i].n_facets; j++)
        {
            ProTKFprintf(fp,"\t\t %d, %d, %d\n", 
                data[i].facets[j][0],data[i].facets[j][1],data[i].facets[j][2]);
        }
        ProTKFprintf(fp,"\t}\n");

                
        ProTKFprintf(fp,"}\n\n");
    
    }     
    
    return(PRO_TK_NO_ERROR);
}

/*=================================================================*\
    FUNCTION : ProTestExportToWireFrame
    PURPOSE  : Output tessellation data to the wire frame file.
\*=================================================================*/
ProError ProTestExportToWireFrame(FILE *fp, ProSurfaceTessellationData *data, ProBoolean opt)
{

    ProError error;
    ProTriangle *trn=NULL;
    ProVector *ver=NULL;    
    int n_v, n_t, i;
        
    error = ProTestExportCollectAllTringles(data, &trn, &ver, opt);
    
    error = ProArraySizeGet((ProArray)ver, &n_v);
    TEST_CALL_REPORT ("ProArraySizeGet()",
        "ProTestExportToWireFrame()", error, error != PRO_TK_NO_ERROR);
    
    if((error != PRO_TK_NO_ERROR)||(n_v<=0))
        return(PRO_TK_BAD_INPUTS);
    
    for(i=0; i<n_v; i++)
        ProTKFprintf(fp,"v %5.5f %5.5f %5.5f\n",ver[i][0], ver[i][1], ver[i][2]);
    
    error = ProArraySizeGet((ProArray)trn, &n_t);
    TEST_CALL_REPORT ("ProArraySizeGet()",
        "ProTestExportToWireFrame()", error, error != PRO_TK_NO_ERROR);
    
    if((error != PRO_TK_NO_ERROR)||(n_t<=0))
        return(PRO_TK_BAD_INPUTS);
    
    for(i=0;i<n_t; i++)
        ProTKFprintf(fp, "f %d %d %d\n", trn[i][0]+1, trn[i][1]+1, trn[i][2]+1);
        
    error = ProArrayFree((ProArray*)&ver);
    TEST_CALL_REPORT ("ProArrayFree()",
        "ProTestExportToWireFrame()", error, error != PRO_TK_NO_ERROR);
        
    error = ProArrayFree((ProArray*)&trn);
    TEST_CALL_REPORT ("ProArrayFree()",
        "ProTestExportToWireFrame()", error, error != PRO_TK_NO_ERROR);
    
    return(PRO_TK_NO_ERROR);
}


/*=================================================================*\
    FUNCTION : ProTestExportModelTo()
    PURPOSE  : Export part tessellation data to the file
\*=================================================================*/
ProError ProTestExportModelTo(ProTestExportData *data)
{   
    ProError error;
    ProSurfaceTessellationData *tes_data = NULL;
    ProMdlName w_name;
    ProCharLine c_name;
    FILE *fp;

    if(data == NULL)
        return(PRO_TK_BAD_INPUTS);

    error = ProPartTessellate ((ProPart)(data->mdl),
                               data->chord_ht,
                               data->angle_cntrl, 
                               data->include_quilts, 
                               &tes_data);
                               
    TEST_CALL_REPORT ("ProPartTessellate()",
        "ProTestExportTo()", error, error != PRO_TK_NO_ERROR);
    if (error!=PRO_TK_NO_ERROR)
        return(error);    

    error = ProMdlMdlnameGet(data->mdl, w_name);
    TEST_CALL_REPORT ("ProMdlMdlnameGet()",
        "ProTestExportTo()", error, error != PRO_TK_NO_ERROR);
    
    ProWstringToString(c_name, w_name);
    switch(data->file_format)
    {
        case EXPORT_MN_WIREFRAME:
            ProTKSprintf(data->file_name,"%s.%s",c_name,"obj");
            break;
        case EXPORT_MN_RENDER:
            ProTKSprintf(data->file_name,"%s.%s",c_name,"slp");
            break;
        case EXPORT_MN_DUMP:
            ProTKSprintf(data->file_name,"%s.%s",c_name,"dump");
            break;
    }
    
    if ((fp = PTApplsUnicodeFopen(data->file_name, "w")) == NULL)
    {
	ProTKPrintf("Cannot open output file\n");
	return (PRO_TK_GENERAL_ERROR);
    }
            
    switch (data->file_format)
    {
        case EXPORT_MN_WIREFRAME:
            error = ProTestExportToWireFrame(fp, tes_data, data->optimization);
            break;
        case EXPORT_MN_RENDER:
            error = ProTestExportToRender(fp, tes_data, ProWstringToString(c_name, w_name));
            break;
        case EXPORT_MN_DUMP:
            error = ProTestExportToDUMP(fp,tes_data);
            break;
    }
    
    error = ProPartTessellationFree(&tes_data);
    TEST_CALL_REPORT ("ProPartTessellationFree()",
        "ProTestExportTo()", error, error != PRO_TK_NO_ERROR);

    fclose(fp);
    return(error);
}

/*=================================================================*\
    FUNCTION : ProTestExportTo()
    PURPOSE  : Export part tessellation data to the file
\*=================================================================*/
ProError ProTestExportTo(ProTestExportData *data)
{   
    ProError error;
    ProMdlType mdl_type;
    int n, n_sel,i;
    ProSelection *sel;
    ProModelitem part_item;    
    static ProUtilMenuButtons  TkMenu[] ={
		{"ExportAsm",  -1, TEST_CALL_PRO_MENU_DELETE},
		{"-As assembly",    EXPORT_MN_ASSM,    0},
		{"-Selected parts", EXPORT_MN_SEL_PART,0},
		{"-Quit",          -1,    TEST_CALL_PRO_MENU_DELETE},
		{"",               -1,                 0}};

    if(data == NULL)
        return(PRO_TK_BAD_INPUTS);

    error = ProMdlTypeGet(data->mdl, &mdl_type);
    TEST_CALL_REPORT ("ProMdlTypeGet()",
        "ProTestExportTo()", error, error != PRO_TK_NO_ERROR);

    switch(mdl_type)
    {    
        case PRO_MDL_ASSEMBLY:
            error = ProUtilMenuIntValueSelect(TkMenu, &n);
            if((error!=PRO_TK_NO_ERROR)||(n<0))
                return(PRO_TK_NO_ERROR);
        
            if (n == EXPORT_MN_SEL_PART) 
            {       
                error = ProSelect((char*)"part",PRO_VALUE_UNUSED,
                    NULL, NULL, NULL, NULL, &sel, &n_sel);
                TEST_CALL_REPORT ("ProSelect()",
                    "ProTestExportTo()", error, error != PRO_TK_NO_ERROR);
                
                if((error!=PRO_TK_NO_ERROR)||(n_sel<=0))
                    return(PRO_TK_E_NOT_FOUND);
                for(i=0; i<n_sel; i++)
                {
                    error = ProSelectionModelitemGet(sel[i], &part_item);
                    TEST_CALL_REPORT ("ProSelectionModelitemGet()",
                        "ProTestExportTo()", error, error != PRO_TK_NO_ERROR);
                    if (error != PRO_TK_NO_ERROR)
                        continue;
                    error = ProModelitemMdlGet (&part_item, (ProMdl*)&(data->mdl));                
                    TEST_CALL_REPORT ("ProModelitemMdlGet()",
                        "ProTestExportTo()", error, error != PRO_TK_NO_ERROR);
                    if (error != PRO_TK_NO_ERROR)
                        continue;
                    error = ProTestExportModelTo(data);
                }
            } else
            {
                error = ProTestExportModelTo(data);
            }       
            break;
        case PRO_MDL_PART:
            error = ProTestExportModelTo(data);
        break;    
        default:
            return(PRO_TK_BAD_INPUTS);
    }
    
    return(error);
}

/*=================================================================*\
    FUNCTION : ProTestExportSetup()
    PURPOSE  : Setup tesselation parameters
\*=================================================================*/
ProError ProTestExportSetup(ProTestExportData *data)
{
    int n;
    ProError error;
    static ProUtilMenuButtons  TkMenu[] ={
		{"ExpSetup",  -1, TEST_CALL_PRO_MENU_DELETE},
		{"-Chord height",  EXPORT_MN_CHORD,    0},
		{"-Angle control", EXPORT_MN_ANGLE,    0},
		{"-Quilt",         EXPORT_MN_QUILT,    0},
		{"-Optimization",  EXPORT_MN_OPT,      0},
		{"-Quit",          -1,    TEST_CALL_PRO_MENU_DELETE},
		{"",      -1,0}};

    error = ProUtilMenuIntValueSelect(TkMenu, &n);

    if((error!=PRO_TK_NO_ERROR)||(n<0))
        return(PRO_TK_NO_ERROR);

    switch(n)
    {
        case EXPORT_MN_CHORD:
            ProUtilMsgPrint("gen", "TEST %0s", "The maximum allowable chord height(>0):");
            error = ProMessageDoubleRead(NULL, &(data->chord_ht));
            break;
        case EXPORT_MN_ANGLE:
            ProUtilMsgPrint("gen", "TEST %0s", "The angle control (between 0.0 and 1.0):");
            error = ProMessageDoubleRead(NULL, &(data->angle_cntrl));
            break;
        case EXPORT_MN_QUILT:
            ProUtilMsgPrint("gen", "TEST %0s", "Include quilt surfaces or not(Y/N):");
            data->include_quilts = (ProBoolean)(ProUtilYesnoGet((char*)"Yes") == PRO_B_TRUE);
            break;
        case EXPORT_MN_OPT:
            ProUtilMsgPrint("gen", "TEST %0s", "Enable optimization(Y/N):");
            data->optimization = (ProBoolean)(ProUtilYesnoGet((char*)"Yes") == PRO_B_TRUE);   
            break;           
    }        
    
    ProTestExportSetup(data);
    
    return(PRO_TK_NO_ERROR);    
}

/*=================================================================*\
    FUNCTION : ProTestExportMainMenu()
    PURPOSE  : Create "TkModify" menu 
\*=================================================================*/
ProError ProTestExportMainMenu(ProMdl mdl)
{
    int n;
    ProError error;
    static ProUtilMenuButtons  TkMenu[] ={
		{"ExportGeom",  -1, TEST_CALL_PRO_MENU_DELETE},
		{"-WireFrame",  EXPORT_MN_WIREFRAME, 0},
		{"-Render",     EXPORT_MN_RENDER,    0},
		{"-Dump",       EXPORT_MN_DUMP,      0},
		{"-Setup",      EXPORT_MN_SETUP,     0},
		{"-Quit",-1,    TEST_CALL_PRO_MENU_DELETE},
		{"",      -1,0}};
    static ProTestExportData data; 
    static int FirstCall =0;               


    if (mdl == NULL)
    {
        error = ProMdlCurrentGet(&mdl);
        TEST_CALL_REPORT ("ProMdlCurrentGet()",
                "ProTestExportMainMenu()", error, error != PRO_TK_NO_ERROR);
        if(error != PRO_TK_NO_ERROR)
            return(error);    
    }       
/*--------------------------------------------------------------------*\
    Initialization default data
\*--------------------------------------------------------------------*/
    if(FirstCall == 0)
    {
        data.chord_ht = 0.1;
        data.angle_cntrl = 0.5;
        data.include_quilts = PRO_B_FALSE;
        data.optimization = PRO_B_FALSE;   
        FirstCall++;
    }    
/*--------------------------------------------------------------------*\
    Show "ExportGeom" menu  
\*--------------------------------------------------------------------*/
    do
    {
        error = ProUtilMenuIntValueSelect(TkMenu, &n);

        if((error!=PRO_TK_NO_ERROR)||(n<0))
            return(PRO_TK_NO_ERROR);
            
        data.mdl = mdl;
    
        switch(n)
        {        
            case EXPORT_MN_WIREFRAME:
            case EXPORT_MN_RENDER:
            case EXPORT_MN_DUMP:
                data.file_format = n;    
                error = ProTestExportTo(&data); 
                break;
            case EXPORT_MN_SETUP:
                error = ProTestExportSetup(&data);
                break;
            
        }            
    } while(1);    
   return(error);    
}