// Moshade - Compute average cross section for 3D shapes
// Copyright (C) 2018-2026 Eric Bechet - bechet@cadxfem.org
//
// See the LICENSE file for license information and contributions.
// Please report all bugs and problems to <bechet@cadxfem.org>.

#ifndef MOSHADE_MESH_OP_H
#define MOSHADE_MESH_OP_H
#include "geom_op.h"
#include <set>
#include <climits>


struct ptsize : public npoint3
{
public :
  ptsize(const npoint3 pt, double r): npoint3(pt),radius(r){}
  double radius;
};

struct less_pts_size
{
  bool operator ()(const ptsize &pt1,const ptsize &pt2) const;
  npoint3 min,max;
};


struct less_pts
{
  bool operator ()(const npoint3 &pt1,const npoint3 &pt2) const;
};

struct edg_uniq
{

  npoint3 pts[2];
  mutable std::vector<int> tri; // connected triangles
  mutable int triorig;
  edg_uniq(const npoint3 &pt1,const npoint3 &pt2);
  int getneighbor(int i,int k=0) const
  {
    int posi=-1;
    int posk=0;
    for (int j=0;j<tri.size();++j)
    {
      if (tri[j]!=i) posk++; else posi=j;
    }
    if (posi!=-1)
    {
      if (k<posi) return tri[k];
      if ((k+1)<tri.size())
        return tri[k+1];
    }
 /*   if (tri[0]==i) return tri[1];
    if (tri[1]==i) return tri[0];*/
    return -1;
  }
};



struct less_edg_uniq
{
  bool operator ()(const edg_uniq &edg1,const edg_uniq &edg2) const;
  npoint3 min,max;
  double tol;
};

struct tri_it
{
  std::set<ptsize,less_pts_size>::iterator pts[3];
  std::set<edg_uniq,less_edg_uniq>::iterator edg[3];
  virtual int ori(const tri_it& other) const; // relative orientation of 2 triangles (0 - no way to determine it, 1 = same, -1 = opposite)
};

class tri_mesh
{
  std::vector<tri_bb> mesh;
  std::vector<npoint3> normal;
  std::vector<tri_it> mesh_it;
  std::set<ptsize,less_pts_size> *pts;  
  std::set<edg_uniq,less_edg_uniq> *edsu;
  less_pts_size compare;
  less_edg_uniq compare_edge;
  npoint3 min;
  npoint3 max; //bounding box
  bool has_topo;
  bool valid_bb;
  double vol; //volume
  bool convex;
  double tol;
  /// find unique entities (vertices + edges) and build topology
  virtual void find_unique_ent(double tol=0.0,bool quiet=false);
  virtual void find_solids(bool normals,bool quiet); // updates volume as well
  virtual void update_bb(int i);

public:
//  tri_mesh():min(0,0,0),max(0,0,0),pts(NULL),eds(NULL),has_topo(false),valid_bb(false),vol(-1.0),convex(false){}
  tri_mesh():min(0,0,0),max(0,0,0),pts(NULL),edsu(NULL),has_topo(false),valid_bb(false),vol(-1.0),convex(false),tol(0.){}
  tri_mesh(std::string filename);
  virtual int size() {return mesh.size();}
  virtual ~tri_mesh() { clear(); }
  /// load stl file
  virtual void load_stl(std::string filename);
  /// load a gm file (descriptive file)
  virtual void load_gm(std::string filename,bool override=false,double szsph=1.0,double szcyl=1.0,int ns=10);
  /// add a new triangle
  virtual void add(tri_bb tr, npoint3 n);
  /// updates bounding box info (clears topology)
  virtual void settol(double tol_) {tol=tol_;}
  virtual void update_bb();
  /// empties data
  virtual void clear();
  virtual bool has_topology(void) const { return has_topo;}
  virtual void clear_topology();
  virtual void build_topology(bool normals,bool quiet);
  virtual void add_sphere(npoint3 center, double r=1.0,int n=10);
  virtual void add_cylinder(npoint3 center1, npoint3 center2, double r=1.0,bool close1=true,bool close2=true,int n=10,npoint3 ori1=npoint3(0,0,0),npoint3 ori2=npoint3(0,0,0),npoint3 dtop=npoint3(0,0,0));
  virtual void add_helix(double R=10, double r=1,int nb_parts=40,int n=10);
  ///apply transformation given by an homogenous transformation matrix
  virtual void transform(const tri_mesh &other, double matf[4][4]);
  ///project along any x, y or z axis
  virtual void project(const tri_mesh &other,int axis);
  /// eliminate triangles facing negative z axis
  virtual void cull(const tri_mesh &other);
  ///compute shaded area for xy mesh (no z coordinate)
  virtual void shade(const tri_mesh &other);
  virtual double area2() const; // area in xy plane !
  virtual double area() const; // area in 3D
  virtual double volume() const; // signed volume in 3D (sign depends on the normals, and those must have been computed with build_topology at least once)
  virtual void display(data_container &data) const;
  virtual void bb(npoint3& bbmin,npoint3& bbmax) const;
};

int shade(double th,double phi,const tri_mesh &mesh,double &sharea,double &prarea,tri_mesh *meshsh=NULL);

#endif  // MOSHADE_MESH_OP_H