// nUtil - An utility Library for gnurbs
// Copyright (C) 2008-2026 Eric Bechet
//
// See the LICENSE file for contributions and license information.
// Please report all bugs and problems to <bechet@cadxfem.org>.
//

#ifndef __NDATA_H
#define __NDATA_H

#include <vector>
#include <string>
#include <ostream>
#include "nutilconfig.h"
#include "npoint.h"


#ifdef OBJ_LOADER
#include "OBJ_Loader.h"
#endif

#ifdef L3MF_LOADER
#include "lib3mf_implicit.hpp"
#endif

class data_container;

class infobase
{
public :
  infobase() : drawable(true),picked(false) {};
  mutable bool drawable;
  mutable bool picked;
  std::string info;
};

class point : public infobase
{
public:
  point() {}
  point(npoint3 pt1) {pts=pt1;}
  npoint3 pts;
};

class line : public infobase
{
public:
  line() {}
  line(npoint3 pt1,npoint3 pt2) {pts[0]=pt1;pts[1]=pt2;}
  npoint3 pts[2];
};

class triangle : public infobase
{
public:
  triangle() {}
  triangle(npoint3 pt1,npoint3 pt2,npoint3 pt3) {pts[0]=pt1;pts[1]=pt2;pts[2]=pt3;}
  npoint3 pts[3];
};

class quad : public infobase
{
public:
  quad() {}
  quad(npoint3 pt1,npoint3 pt2,npoint3 pt3,npoint3 pt4) {pts[0]=pt1;pts[1]=pt2;pts[2]=pt3;pts[3]=pt4;}
  npoint3 pts[4];
};

class stltriangle: public triangle // triangle + normal
{
public :
  stltriangle() {}
  stltriangle(npoint3 pt1,npoint3 pt2,npoint3 pt3) {pts[0]=pt1;pts[1]=pt2;pts[2]=pt3;}
  npoint3 normal;
  unsigned int tag;
};

struct neighbors_t
{
  std::vector<int> tab; // indices of all the triangles connected to a given indexed element
  std::vector<int> tabpts; // indices of the all the vertices of the element
  unsigned int tag;
};

class stlmesh : public infobase
{ 
public:
  std::string info;
  std::vector<stltriangle> facets; //the triangles
  std::vector<line> boundaries; // edges corresponding to boundaries (1 neighbor)
  std::vector<line> non_manifold; // edges that are connected to more than 2 neighbors
  std::vector<neighbors_t> neighbors; // connectivity : see above
  std::vector<std::pair<npoint3,std::vector<int> > > vertices; // all the vertices, plus the index of the triangles 
                                                               // to which each vertex belong to
  unsigned long load_ascii_stl(std::string filename);
  unsigned long load_binary_stl(std::string filename);
  bool is_stl_ascii(std::string filename);
  void build_neigborhood(void);
  unsigned long load_stl(std::string filename);
  void Display(data_container & data,bool normals=false);
  virtual void clear()
  {
    info.clear();
    facets.clear();
    boundaries.clear();
    non_manifold.clear();
    neighbors.clear();
    vertices.clear();
  }
};


#ifdef OBJ_LOADER
class objmesh : public infobase
{ 
  objl::Loader loader;
public:
  unsigned long load_obj(std::string filename);
  void Display(data_container & data,bool normals=false);
};
#endif


#ifdef L3MF_LOADER
class mfmesh : public infobase
{ 
  Lib3MF::PWrapper wrapper;
  Lib3MF::PModel model;
public:
  mfmesh() { wrapper = Lib3MF::CWrapper::loadLibrary();  model = wrapper->CreateModel();}
  unsigned long load_3mf(std::string filename);
  void Display(data_container & data,bool normals=false);
};
#endif

class nmesh
{
public:
  enum eltype {NIL=0, POINT,LINE,TRIANGLE,TETRAHEDRON};
  struct nnode
  {
    nnode(void) : xyz(),uvw(),num(-1) {}
    nnode(int num_) : xyz(),uvw(),num(num_) {}
    nnode(int num_,const npoint3 &pt_) : xyz(pt_),uvw(),num(num_) {}
    nnode(int num_,const npoint3 &pt_,const npoint3 &pt_uvw) : xyz(pt_),uvw(pt_uvw),num(num_) {}
    nnode(const npoint3 &pt_) : xyz(pt_),uvw(),num(-1) {}
    npoint3 xyz;
    npoint3 uvw;
    long num;
  };
  struct nelement
  {
    nelement(eltype t) : type(t),num(-1)
    {
      for (int i=0;i<4;++i) pts[i]=-1;
    }
    nelement(const nelement &other) : type(other.type),num(-1)
    {
      for (int i=0;i<4;++i) pts[i]=other.pts[i];
    }
    int pts[4];
    eltype type;
    long num;
  };
  int add_node(const nnode& src)
  {
    nodes.push_back(src);
    return nodes.size()-1;
  }
  int nb_nodes(void) const
  {
    return nodes.size();
  }
  nnode& get_node(size_t i)
  {
    return nodes[i];
  }
  const nnode& get_node(size_t i) const
  {
    return nodes[i];
  }
  void clear(void)
  {
    nodes.clear();
    elements.clear();
  }

  int add_element(const nelement& src)
  {
    elements.push_back(src);
    return elements.size()-1;
  }
  int nb_elements(void) const
  {
    return elements.size();
  }
  nelement& get_element(size_t i)
  {
    return elements[i];
  }
  const nelement& get_element(size_t i) const
  {
    return elements[i];
  }
private:
  std::vector<nnode> nodes;
  std::vector<nelement> elements;
};

struct color
{
  color(unsigned char r=255,unsigned char g=255,unsigned char b=255,unsigned char a=255) : R(r),G(g),B(b),A(a) {}
  unsigned char R;
  unsigned char G;
  unsigned char B;
  unsigned char A; //Alpha
};

struct properties
{
  properties() : c(),thickness(1.),pointsize(1.),edgeon(),edgethickness(1.),edgecolor(),teston(),nb_uid(0) {}
  color c;
  float thickness;
  float pointsize;
  bool edgeon;
  float edgethickness;
  color edgecolor;
  bool teston;
  int nb_uid; //0, 1, 2 or 3
  int uids[3];// max=3 user ids...
};

typedef std::vector<std::pair<properties,int> > container_props;

class data_container
{
  std::vector<point> points;
  container_props proppoints;
  std::vector<line> lines;
  container_props proplines;
  std::vector<triangle> triangles;
  container_props proptriangles;
  std::vector<quad> quads;
  container_props propquads;
  std::vector<std::pair<point,color> > texts[3];
  mutable bool _has_changed;
  
public :
  data_container();
  data_container(const data_container& other); // copy constructor
  virtual ~data_container(){}
  void clear(void);
  bool has_changed(void) const {return _has_changed;}
  bool changed(bool b=false) const;
  const color& getcolorpoints(int i=0) const;
  const color& getcolorlines(int i=0) const;
  const color& getcolortriangles(int i=0) const;
  const color& getcolorquads(int i=0) const;  

  const properties& getproppoints(int i) const; 
  const properties& getproplines(int i) const;
  const properties& getproptriangles(int i) const;
  const properties& getpropquads(int i) const;

  const properties getproppoints() const;
  const properties getproplines() const;
  const properties getproptriangles() const;
  const properties getpropquads() const;

  int getindexpoints(int i=0) const;
  int getindexlines(int i=0) const;
  int getindextriangles(int i=0) const;
  int getindexquads(int i=0) const;


  int getnumproppoints(void) const;
  int getnumproplines(void) const;
  int getnumproptriangles(void) const;
  int getnumpropquads(void) const;


  void setcolorpoints(color c);
  void setcolorlines(color c);
  void setcolortriangles(color c);
  void setcolorquads(color c);

  void setproppoints(properties p);
  void setproplines(properties p);
  void setproptriangles(properties p);
  void setpropquads(properties p);
  void setpropall(properties p);
  
  int add_point(const npoint3& src);
  int add_point(const point& src);
  int add_line(const line& src);
  int add_triangle(const triangle& src);
  int add_quad(const quad& src);
  int add_text(int num,const std::pair<point,color>& src);

  int nb_points(void) const;
  int nb_lines(void) const;
  int nb_triangles(void) const;
  int nb_quads(void) const;
  int nb_texts(int num) const;

  const point& get_point(int i) const;
  const line& get_line(int i) const;
  const triangle& get_triangle(int i) const;
  const quad& get_quad(int i) const;
  const point& get_text(int num,int i) const;
  const color& get_text_color(int num,int i) const;
  point& get_point(int i);
  line& get_line(int i);
  triangle& get_triangle(int i);
  quad& get_quad(int i);
  point& get_text(int num,int i);
  color& get_text_color(int num,int i);
  void merge(const data_container & other);
  void unhide(void) const;
  virtual void print(std::ostream& stream) const;
  virtual int read(std::istream& stream);
};


#endif // __NDATA_H
