/*
 * Copyright (c) 1991-2024 by STEP Tools Inc. 
 * All Rights Reserved.
 * 
 * Permission to use, copy, modify, and distribute this software and
 * its documentation is hereby granted, provided that this copyright
 * notice and license appear on all copies of the software.
 * 
 * STEP TOOLS MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE
 * SUITABILITY OF THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING
 * BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. STEP TOOLS
 * SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A
 * RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
 * DERIVATIVES.
 *
 * Author: David Loffredo (loffredo@steptools.com)
 */

// Sample program to facet  step assembly

#include <stp_schema.h>
#include <stix.h>

// When this flag is 0, the print_mesh_details() function only prints
// some summary information about each mesh.  When set to 1, it will
// call print_triangle() to print the coordinates of each triangle,
// which produces a large amount of output!
//
int PRINT_ALL_TRIANGLES = 0;

static void print_mesh_for_product (
    stp_product_definition * pd
    );

static void print_mesh_for_shape (
    stp_representation * rep,
    RoseXform &xform,
    RoseObject * rep_rel_or_mapped_item,
    stp_product_definition * pd,   // null if not a new product
    unsigned nest_depth 	   // for indenting
    );

static void print_mesh_details (
    const RoseMesh * mesh, 
    RoseXform &xform,
    stp_representation_item * solid,
    const char * pfx
    );

static void print_triangle (
    const RoseMesh * fs,
    RoseXform &xform,
    unsigned facet_num,
    const char * pfx
    );

static void print_transform (
    RoseXform &xform,
    const char * pfx
    );



int main (int /*argc*/, char ** /*argv*/)
{
    ROSE.quiet(1);
    stplib_init();	// initialize step aim library
    stix_mesh_init();

    const char * srcfile = "as1-ac-214.stp";

    RoseDesign * d = ROSE.findDesign(srcfile);
    if (!d) {
	printf ("Could not open STEP file %s\n", srcfile);
	exit (1);
    }

    // prepare for working with assemblies
    rose_compute_backptrs(d);
    stix_asm_tag(d);

    // Build meshes for everything in parallel, returns when
    // everything is finished.  The options structure can be omitted,
    // but is where you can control aspects of the meshing.
    RoseMeshOptions opts;

    stix_mesh_make_all(d, &opts);

    // Navigate the shape tree of the assembly to each shape in
    // context with its assembly placement.  Print the mesh details
    // and placement info.
    //
    unsigned i,sz;
    StpAsmProductDefVec roots;
    stix_find_root_products (&roots, d);
    for (i=0, sz=roots.size(); i<sz; i++)
    {
	print_mesh_for_product (roots[i]);
    }
    return 0;
}




//------------------------------------------------------------
//------------------------------------------------------------
// PRINT THE FACET INFORMATION -- This follows the shape information
// attached to a single product or assembly and prints it to stdout.
//
// Since the shapes are in a tree that parallels the product tree, we
// look for attached next_assembly_usage_occurrences (NAUO) that tell
// us when we are moving into the shape of another product.
//
// Since this code touches all of the facets and transforms them with
// any assembly placements, you can adapt this code as needed to feed
// a display system or some other application instead of printing.
//------------------------------------------------------------
//------------------------------------------------------------


void print_mesh_for_product (
    stp_product_definition * pd
    ) 
{
    // Print the shape tree for each shape associated with a product,
    // and then follow the shape tree downward.  At each level we
    // check the shape relationship for a link to product relations
    // because shape side because there can be relationships there
    // that are not linked to products.

    unsigned i, sz;

    if (!pd) return;

    StixMgrAsmProduct * pm = StixMgrAsmProduct::find(pd);
    stp_product_definition_formation * pdf = pd-> formation();
    stp_product * p = pdf? pdf-> of_product(): 0;

    const char * pname = p? p-> name(): 0;
    if (!pname || !*pname) pname = "[no name]";

    printf ("ROOT PRODUCT #%lu - %s\n", pd-> entity_id(), pname);
    for (i=0, sz=pm->shapes.size(); i<sz; i++) 
    {
	if (i>0) {
	    printf ("------------------------------\n");
	    printf ("Alternate shape tree #%u for ROOT PRODUCT #%lu - %s\n", 
		    i, pd-> entity_id(), pname);
	}

	// The root placement is usually the identity matrix but some
	// systems put a standalone AP3D at the top to place the whole
	// thing in the global space.
	RoseXform starting_placement;
	stp_shape_representation * rep = pm->shapes[i];

	StixMgrAsmShapeRep * rep_mgr = StixMgrAsmShapeRep::find(rep);
	if (rep_mgr && rep_mgr->root_placement) {
	    stix_xform_put(starting_placement, rep_mgr->root_placement);

	    printf ("Using Custom Coordinate System defined by #%lu\n", 
		    rep_mgr->root_placement-> entity_id());
	}

	print_mesh_for_shape (rep, starting_placement, 0, 0, 0);
    }
}




void print_mesh_for_shape (
    stp_representation * rep,
    RoseXform &rep_xform,
    RoseObject * rep_rel_or_mapped_item,
    stp_product_definition * pd,   // null if not a new product
    unsigned nest_depth 	   // for indenting
    )
{
    unsigned i, sz;
    RoseStringObject nest_indent = "";

    if (!rep) return;

    // indent for a nice tree printout
    if (pd) nest_depth++;
    for (i=0; i<nest_depth; i++) 
	nest_indent += "\t";


    // print the usage of a particular shape, given the relation that
    // hooks them together geometrically.  This relation will give the
    // transform for the shape.

    // Display the mesh information for the shape

    if (pd) {
	stp_product_definition_formation * pdf = pd-> formation();
	stp_product * p = pdf? pdf-> of_product(): 0;

	const char * pname = p? p-> name(): 0;
	if (!pname || !*pname) pname = "[no name]";

	fputs (nest_indent.as_const(), stdout);
	fputs ("--------------------\n", stdout);
	fputs (nest_indent.as_const(), stdout);
	printf ("PRODUCT #%lu - %s\n", pd-> entity_id(), pname);
    }
    
    // Does the rep have any meshed items?  In an assembly, some reps
    // just contain placements for transforming components. If there
    // are solids, we should have previously generated meshes.
    //
    fputs ("\n", stdout);
    fputs (nest_indent.as_const(), stdout);
    printf ("Shape #%lu (%s)\n", rep-> entity_id(), rep-> domain()-> name());

    fputs (nest_indent.as_const(), stdout);
    printf ("placement -- \n");
    print_transform (rep_xform, nest_indent.as_const());


    SetOfstp_representation_item * items = rep->items();
    unsigned solids_printed = 0;
    for (i=0, sz=items->size(); i<sz; i++) 
    {
	stp_representation_item  * it = items->get(i);
	StixMesh * mb = stix_mesh_find (it, rep);
	if (mb) {
	    print_mesh_details (mb, rep_xform, it, nest_indent.as_const());
	    solids_printed++;
	}
    }

    if (!solids_printed) {
	fputs (nest_indent.as_const(), stdout);
	printf (" -- no meshes in representation --\n");
    }

    // Go through all of the child shapes which can be attached by a
    // shape_reprepresentation_relationship or a mapped_item.  If the
    // relation has a NAUO associated with it, then it is the start of
    // a different product, otherwise it is still part of the shape of
    // this one.
    //
    StixMgrAsmShapeRep * rep_mgr = StixMgrAsmShapeRep::find(rep);
    if (!rep_mgr) return;


    for (i=0, sz=rep_mgr->child_rels.size(); i<sz; i++) 
    {
	stp_shape_representation_relationship * rel = rep_mgr->child_rels[i];
	stp_representation * child = stix_get_shape_usage_child_rep (rel);
	stp_product_definition * cpd = 
	    stix_get_shape_usage_child_product (rel);

	// Move to location in enclosing asm
	RoseXform child_xform = stix_get_shape_usage_xform (rel);
	rose_xform_compose(child_xform, rep_xform, child_xform);

	print_mesh_for_shape (child, child_xform, rel, cpd, nest_depth);
    }



    for (i=0, sz=rep_mgr->child_mapped_items.size(); i<sz; i++) 
    {
	stp_mapped_item * rel = rep_mgr->child_mapped_items[i];
	stp_representation * child = stix_get_shape_usage_child_rep (rel);
	stp_product_definition * cpd = 
	    stix_get_shape_usage_child_product (rel);

	// Move to location in enclosing asm
	RoseXform child_xform = stix_get_shape_usage_xform (rel);
	rose_xform_compose(child_xform, rep_xform, child_xform);

	print_mesh_for_shape (child, child_xform, rel, cpd, nest_depth);
    }

}


void print_mesh_details (
    const RoseMesh * mesh, 
    RoseXform &mesh_xform,
    stp_representation_item * solid,
    const char * pfx
    )
{
    fputs ("\n", stdout);
    fputs (pfx, stdout);
    printf ("Solid #%lu (%s)\n", 
	    solid-> entity_id(), 
	    solid-> domain()-> name()
	);

    if (!mesh) {
	fputs (pfx, stdout);
	printf (" -- no mesh --\n");
    }
    else 
    {
	fputs (pfx, stdout);
	printf (
	    "mesh has %u triangles\n", 
	    mesh-> getFacetCount()
	    );

	fputs (pfx, stdout);
	printf (
	    "step faces: %u , step edges: %u\n", 
	    mesh-> getFaceCount(), 
	    mesh-> getEdgeCount()
	    );

	fputs (pfx, stdout);
	printf (
	    "step global uncertainty: %g\n", 
	    mesh-> getOptions().getUncertainty()
	    );

	// Print all of the triangles - Flip the flag below if you
	// want this.  It produces a large amount of output!
	//
	if (PRINT_ALL_TRIANGLES)
	{
	    unsigned i, sz;

	    for (i=0, sz=mesh->getFacetCount(); i< sz; i++)
		print_triangle (mesh, mesh_xform, i, pfx);
	}
    }
}




void print_triangle (
    const RoseMesh * fs,
    RoseXform &xform,
    unsigned facet_num,
    const char * pfx
    )
{
    double v[3];
    double n[3];
    const RoseMeshFacet * f = fs-> getFacet(facet_num);

    fputs (pfx, stdout);
    printf("facet %u\n", facet_num);

    if (!f) return;


    // The components of the triangle verticies and vertex normals are
    // given by an index into internal tables.  Apply the transform so
    // that the coordinates and directions are placed correctly.

    fs-> getVertex(v, f-> verts[0]);
    fs-> getNormal(n, f-> normals[0]);
    rose_xform_apply (v, xform, v);
    rose_xform_apply_dir (n, xform, n);

    fputs (pfx, stdout);
    printf("v1: (%.6g %.6g %.6g)\t", v[0], v[1], v[2]);
    printf("n1: (%.3f %.3f %.3f)\n", n[0], n[1], n[2]);

    fs-> getVertex(v, f-> verts[1]);
    fs-> getNormal(n, f-> normals[1]);
    rose_xform_apply (v, xform, v);
    rose_xform_apply_dir (n, xform, n);

    fputs (pfx, stdout);
    printf("v2: (%.6g %.6g %.6g)\t", v[0], v[1], v[2]);
    printf("n2: (%.3f %.3f %.3f)\n", n[0], n[1], n[2]);

    fs-> getVertex(v, f-> verts[2]);
    fs-> getNormal(n, f-> normals[2]);
    rose_xform_apply (v, xform, v);
    rose_xform_apply_dir (n, xform, n);

    fputs (pfx, stdout);
    printf("v3: (%.6g %.6g %.6g)\t", v[0], v[1], v[2]);
    printf("n3: (%.3f %.3f %.3f)\n", n[0], n[1], n[2]);
}




void print_transform (
    RoseXform &xf,
    const char * pfx
    )
{
    fputs (pfx, stdout);
    printf("Loc: (%.6g %.6g %.6g)\n",
	   xf.origin().x(), 
	   xf.origin().y(), 
	   xf.origin().z());
    fputs (pfx, stdout);
    printf("zdir (%.3f %.3f %.3f)\n",
	   xf.zdir().i(), 
	   xf.zdir().j(), 
	   xf.zdir().k());
    fputs (pfx, stdout);
    printf("xdir (%.3f %.3f %.3f)\n", 
	   xf.xdir().i(), 
	   xf.xdir().j(), 
	   xf.xdir().k());
}