// 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>.
//

#include "ndata.h"
#include "linear_algebra.h"
#include <fstream>
#include <unordered_map>
#include <unordered_set>
#include <map>
#include <cstdint>

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

int my_stricmp ( const char *s1, const char*s2 );
bool is_little_endian(void);
float swapfloat(char b[4]);
uint32_t swaplong(char b[4]);
uint16_t swapint(char b[2]);

bool stlmesh::is_stl_ascii(std::string filename)
{
  std::ifstream fe;
  char poub1[1024];
  char poub2[1024];
  fe.open ( filename.c_str());
  if ( !fe.fail() )
  {
    fe.width(1024);
    fe>>poub1;
    fe.seekg(0,std::ios::beg);
    if (my_stricmp ( poub1,"solid" ) ==0 )
    {
      fe.getline ( poub2,255 );
      fe >> poub1 >> poub2;
      fe.close();
      if ( my_stricmp ( poub1,"facet" ) ==0 )
        return true;
      else
        return false;
    }
    else
    {
      fe.close();
      return false;
    }
  }
  else
  {
    std::cerr << " fichier inexistant" << std::endl;
    throw 1;
  }
}

unsigned long stlmesh::load_ascii_stl(std::string filename)
{
  std::ifstream fe;
  char poub1[1024];
  char poub2[1024];
  long i,j;
  stltriangle t;
  fe.open ( filename.c_str() );
  if ( !fe.fail() )
  {
    fe.width(1024);
    fe>>poub1; // solid
    fe.seekg(0,std::ios::beg);
    if ( my_stricmp ( poub1,"solid" ) ==0 )
    {
      fe.getline ( poub2,255 );
      info = poub2;
      fe >> poub1 >> poub2; // facet normal
      fe >> t.normal[0] >> t.normal[1] >> t.normal[2] ;
      while ( !fe.eof() )
      {
        if ( ( my_stricmp ( poub1,"endsolid" ) ==0 ) )
          break;
        if ( ( my_stricmp ( poub1,"facet" ) !=0 ) || ( my_stricmp ( poub2,"normal" ) !=0 ) )
        {
          fe.close();
          throw 1;
          break;
        }
        fe >> poub1 >> poub2; // outer loop
        if ( ( my_stricmp ( poub1,"outer" ) !=0 ) )
        {
          fe.close();
          throw 1;
          break;
        }
        for ( j=0; j<3; ++j )
        {
          fe >> poub1; // vertex
          fe >> t.pts[j][0] >> t.pts[j][1] >> t.pts[j][2];
          if ( ( my_stricmp ( poub1,"vertex" ) !=0 ) )
          {
            fe.close();
            throw 1;
            break;
          }
        }
        t.tag=0;
        facets.push_back(t);
        fe >> poub1 >> poub2; //endloop \n endfacet
        fe >> poub1 >> poub2; // facet normal
        fe >> t.normal[0] >> t.normal[1] >> t.normal[2] ;
      }
      fe.close();
    }
    else
    {
      fe.close();
      throw 1;
    }
  }
  else
  {
    std::cerr << " fichier inexistant" << std::endl;
    throw 1;
  }
  return facets.size();
}

unsigned long stlmesh::load_binary_stl(std::string filename)
{
  char * data;
  char datasz[4];
  char dataname[81];
  std::ifstream fe;
  uint32_t sz(0);
  info.resize(80);
  const bool is_LE = is_little_endian();
  fe.open ( filename.c_str(), std::ios::in|std::ios::binary);
  if ( !fe.fail() )
  {
    fe.read(dataname,80);
    dataname[80]='\0';
    info=dataname;
    fe.read(datasz,4);
    if (is_LE) sz=*(uint32_t*)datasz; else sz=swaplong(datasz);
    if (sz>0)
    {
      data=new char[sz*50] ;
      fe.read(data,50*sz);
      if (fe)
      {
        facets.resize(sz);
        for (unsigned long i=0;i<sz;++i)
        {
          char *ba_nor=data+i*50; // adresse normale
          char *ba_pts=data+i*50+12; // adresse points
          char *ba_nfo=data+i*50+48; // adresse info
          float* nor=(float* ) ba_nor;
          float* pts=(float* ) ba_pts;
          uint16_t *nfo=(uint16_t*) ba_nfo; // should be 0, in fact. if not, the format is undefined...
          if (is_LE) 
            facets[i].tag=*nfo;
          else
            facets[i].tag=swapint(ba_nfo);
          for (int j=0;j<3;++j) 
          {
            if (is_LE)
              facets[i].normal[j]=nor[j];
            else
              facets[i].normal[j]=swapfloat(&ba_nor[j*4]);
          } 
          for (int j=0;j<3;++j) 
          {
            for (int k=0;k<3;++k)
            {
              if (is_LE)
                facets[i].pts[j][k]=pts[j*3+k];
              else
                facets[i].pts[j][k]=swapfloat(&ba_pts[(j*3+k)*4]);
            }
          }
        }
      } else sz=0;
      delete data;
    }
    else
      sz=0;
  }
  else
  {
    std::cerr << " fichier inexistant" << std::endl;
    throw 1;
  }
  fe.close();
  return facets.size();
}

unsigned long stlmesh::load_stl(std::string filename)
{
  unsigned long n;
  clear();
  if (is_stl_ascii(filename))
    n=load_ascii_stl(filename);
  else
    n=load_binary_stl(filename);
  build_neigborhood();
  return n;
}

#ifdef L3MF_LOADER
unsigned long mfmesh::load_3mf(std::string filename)
{
  Lib3MF::PReader reader = model->QueryReader("3mf");
  reader->SetStrictModeActive(false);
  reader->ReadFromFile(filename);
  for (Lib3MF_uint32 iWarning = 0; iWarning < reader->GetWarningCount(); iWarning++) 
  {
    Lib3MF_uint32 nErrorCode;
    std::string sWarningMessage = reader->GetWarning(iWarning, nErrorCode);
    std::cout << "Encountered warning #" << nErrorCode << " : " << sWarningMessage << std::endl;
  }
  return 1;
}


void __Display( data_container& data , bool normals,Lib3MF::PMeshObject meshObject,Square_Matrix &tr)
{
//  Lib3MF_uint64 nVertexCount = meshObject->GetVertexCount();
  Lib3MF_uint64 nTriangleCount = meshObject->GetTriangleCount();
  for (Lib3MF_uint64 i=0;i<nTriangleCount;++i)
  {
    Lib3MF::sTriangle tri=meshObject->GetTriangle(i);
    triangle t;
    for (int j=0;j<3;++j)
    {
      Lib3MF::sPosition pos=meshObject->GetVertex(tri.m_Indices[j]);
      npoint3 p;
      Vector pp({0,0,0,1}),pp2(4);
      for (int k=0;k<3;++k)
      {
        pp(k)=pos.m_Coordinates[k];
      }
      tr.Mult(pp,pp2);
      for (int k=0;k<3;++k)
        p[k]=pp2(k);
      t.pts[j]=p;
    }
    data.add_triangle(t);
  }
}

void mfmesh::Display( data_container& data , bool normals)
{
  Lib3MF::PObjectIterator objectIterator = model->GetObjects();
  std::map<Lib3MF_uint32,Lib3MF::PObject> objects;
  
  while (objectIterator->MoveNext())
  {
    Lib3MF::PObject object = objectIterator->GetCurrentObject();
    objects[object->GetResourceID()]=object;
  }
  
  Lib3MF::PBuildItemIterator buildItemIterator = model->GetBuildItems();
  while (buildItemIterator->MoveNext())
  {
    Lib3MF::PBuildItem buildItem = buildItemIterator->GetCurrent();
    Lib3MF_uint32 objnum=buildItem->GetObjectResourceID();
    Square_Matrix tr1({{1,0,0,0},{0,1,0,0},{0,0,1,0},{0,0,0,1}});
    Square_Matrix tr2({{1,0,0,0},{0,1,0,0},{0,0,1,0},{0,0,0,1}});
    Square_Matrix tr({{1,0,0,0},{0,1,0,0},{0,0,1,0},{0,0,0,1}});
    if (buildItem->HasObjectTransform())
    {
        sLib3MFTransform tra=buildItem->GetObjectTransform();
        for (int i=0;i<3;++i)
          for (int j=0;j<4;++j)
            tr1(i,j)=tra.m_Fields[j][i];
//        tr1.Display();
    }
    Lib3MF::PObject object=objects[objnum];
    Lib3MF::PMeshObject meshObject;
    
    if (object->IsMeshObject())
    {
      meshObject=model->GetMeshObjectByID(object->GetResourceID());
      properties pr=data.getproptriangles();
      data.setcolortriangles(color(255,255,255));
      __Display(data,normals,meshObject,tr1);
      data.setproptriangles(pr);
    }
    if (object->IsComponentsObject())
    {
      Lib3MF::PComponentsObject componentsObject=model->GetComponentsObjectByID(object->GetResourceID());
      for (Lib3MF_uint32 nIndex = 0; nIndex < componentsObject->GetComponentCount(); nIndex++)
      {
        Lib3MF::PComponent component=componentsObject->GetComponent(nIndex);
        if (component->HasTransform())
        {
          sLib3MFTransform tra=component->GetTransform();
          for (int i=0;i<3;++i)
            for (int j=0;j<4;++j)
              tr2(i,j)=tra.m_Fields[j][i];
//          tr2.Display();
        }
        tr.Mult(tr1,tr2);
        Lib3MF_uint32 meshid=component->GetObjectResourceID();
        meshObject=model->GetMeshObjectByID(meshid);
        properties pr=data.getproptriangles();
        data.setcolortriangles(color(0,255,0));
        __Display(data,normals,meshObject,tr);
        data.setproptriangles(pr);
      }
    }
  }
}
#endif

#ifdef OBJ_LOADER
unsigned long objmesh::load_obj(std::string filename)
{
  loader.LoadFile(filename);
  return 1;
}


void objmesh::Display( data_container& data , bool normals)
{
  for (int i = 0; i < loader.LoadedMeshes.size(); i++)
  {
    // Copy one of the loaded meshes to be our current mesh
    objl::Mesh curMesh = loader.LoadedMeshes[i];
    // Print Mesh Name
    // file << "Mesh " << i << ": " << curMesh.MeshName << "\n";
    // Print Vertices
    // file << "Vertices:\n";

    // Go through each vertex and print its number,
    //  position, normal, and texture coordinate
    for (int j = 0; j < curMesh.Vertices.size(); j++)
    {
      data.add_point(npoint3(curMesh.Vertices[j].Position.X,curMesh.Vertices[j].Position.Y,curMesh.Vertices[j].Position.Z));
/*      file << "V" << j << ": " << "P(" << curMesh.Vertices[j].Position.X << ", " << curMesh.Vertices[j].Position.Y << ", " << curMesh.Vertices[j].Position.Z << ") " <<
    "N(" << curMesh.Vertices[j].Normal.X << ", " << curMesh.Vertices[j].Normal.Y << ", " << curMesh.Vertices[j].Normal.Z << ") " <<
    "TC(" << curMesh.Vertices[j].TextureCoordinate.X << ", " << curMesh.Vertices[j].TextureCoordinate.Y << ")\n";*/
    }

    // Print Indices

    // Go through every 3rd index and print the
    //	triangle that these indices represent
    for (int j = 0; j < curMesh.Indices.size(); j += 3)
    {
      npoint3 p1(curMesh.Vertices[curMesh.Indices[j]].Position.X,curMesh.Vertices[curMesh.Indices[j]].Position.Y,curMesh.Vertices[curMesh.Indices[j]].Position.Z);
      npoint3 p2(curMesh.Vertices[curMesh.Indices[j+1]].Position.X,curMesh.Vertices[curMesh.Indices[j+1]].Position.Y,curMesh.Vertices[curMesh.Indices[j+1]].Position.Z);
      npoint3 p3(curMesh.Vertices[curMesh.Indices[j+2]].Position.X,curMesh.Vertices[curMesh.Indices[j+2]].Position.Y,curMesh.Vertices[curMesh.Indices[j+2]].Position.Z);
      triangle t(p1,p2,p3);
      data.add_triangle(t);
/*      file << "T" << j / 3 << ": " << curMesh.Indices[j] << ", " << curMesh.Indices[j + 1] << ", " << curMesh.Indices[j + 2] << "\n";*/
    }
  }
}



#endif

class ordered_edge
{
  int _a;
  int _b;
public :
  int a(void) const { return _a;}
  int b(void) const { return _b;}
  mutable std::vector<int> tris;
  ordered_edge(int a_,int b_)  { if (a_<b_) {_a=a_;_b=b_;} else {_a=b_;_b=a_;} }
  friend bool operator<(const ordered_edge& l, const ordered_edge& r)
  {
      return std::tie(l._a, l._b)
           < std::tie(r._a, r._b); // keep the same order
  }
  friend bool operator==(const ordered_edge& l, const ordered_edge& r)
  {
      return std::tie(l._a, l._b)
           == std::tie(r._a, r._b);
  }
  friend struct std::hash<ordered_edge>;
};

template<>
struct std::hash<ordered_edge>
{
  std::size_t operator()(ordered_edge const & s) const noexcept
  {
    std::size_t seed=0;
    my_hash_combine(seed,s._a);
    my_hash_combine(seed,s._b);
    return seed;
  }
};


void stlmesh::build_neigborhood(void)
{
  std::unordered_map<npoint3,int> tabpts;

  vertices.clear();
  vertices.reserve(facets.size()/2);
  std::unordered_set<ordered_edge> tabedges;
  int nb=0;
  std::cout << facets.size() << " triangles" <<std::endl;
  neighbors.resize(facets.size());
  for(int i=0;i<facets.size();++i)
  {
    neighbors[i].tab.reserve(3);
    neighbors[i].tabpts.reserve(3);
    int num[3];
    for(int j=0;j<3;++j)
    {
      auto result = tabpts.insert(std::make_pair(facets[i].pts[j], 0));
      if (result.second)
      {
        result.first->second=nb;
        num[j]=nb;
        std::pair<npoint3,std::vector<int> > pp;
        pp.first=facets[i].pts[j];pp.second.reserve(8);pp.second.push_back(i);
        vertices.push_back(pp);
        neighbors[i].tabpts.push_back(nb);
        nb++;
      } else 
      {
        num[j]=result.first->second;
        vertices[result.first->second].second.push_back(i);
        neighbors[i].tabpts.push_back(result.first->second);
      }
//      std::cout << num[j] << " ";
    }
//    std::cout << std::endl ;
    for(int j=0;j<3;++j)
    {
      ordered_edge edg(num[j],num[(j+1)%3]);
      auto result = tabedges.insert(edg);
      result.first->tris.push_back(i);
    }
  }
  int nbbnd=0,nbnm=0;
  for (auto it=tabedges.begin();it!=tabedges.end();++it)
  {
    if (it->tris.size() >= 2)
    {
      for (int i=0;i<it->tris.size();++i)
      {
        for (int j=i+1;j<it->tris.size();++j)
        {
          neighbors_t &n1=neighbors[it->tris[i]];
          neighbors_t &n2=neighbors[it->tris[j]];
          n1.tab.push_back(it->tris[j]);
          n2.tab.push_back(it->tris[i]);
        }
      }
    }
    if (it->tris.size() > 2) { nbnm++; non_manifold.push_back(line(vertices[it->a()].first,vertices[it->b()].first));}
    if (it->tris.size() ==1) { nbbnd++; boundaries.push_back(line(vertices[it->a()].first,vertices[it->b()].first));}
    if (it->tris.size() ==0) std::cout << "error " << std::endl;
  }
  
  std::cout << tabpts.size() << " vertices" <<std::endl;
  std::cout << tabedges.size() << " edges" <<std::endl;
  std::cout << nbnm << " non manifold edges " << std::endl;
  std::cout << nbbnd << " boundary edges " << std::endl;
  std::cout << "V-E+F = 2(S-H)+R = " << (int)tabpts.size() + (int)facets.size() - (int)tabedges.size() << std::endl;
}


void stlmesh::Display( data_container& data , bool normals)
{
  for(int i=0;i<facets.size();++i)
    data.add_triangle(facets[i]);
  for(int i=0;i<boundaries.size();++i)
    data.add_line(boundaries[i]);
  properties p=data.getproplines();
  data.setcolorlines(color(255,0,0));
  for(int i=0;i<non_manifold.size();++i)
    data.add_line(non_manifold[i]);
  data.setproplines(p);
  
  if (normals)
  {
    for(int i=0;i<facets.size();++i)
    { 
      double dmin=-1;
      npoint3 center(0,0,0);
      for( int j=0;j<3;++j)
      {
        center+=(facets[i].pts[j])/3.;
        double d2=(facets[i].pts[j]-facets[i].pts[(j+1)%3]).norm();
        if (dmin>=0)
        {
          if (d2<dmin)
            dmin=d2;
        }
        else 
          dmin=d2;
      }
      double len=facets[i].normal.norm();
      if (len==0) len=1.0;
      line l(center,center+facets[i].normal*dmin/(3.*len));
//      point p=center;
//      data.add_point(p);
      data.add_line(l);

    }
  }
}


std::ostream& operator<<(std::ostream& stream, 
                     const data_container& data)
{
  data.print(stream);
  return stream;
}

bool data_container::changed(bool b) const 
{
  bool tmp=_has_changed;
  _has_changed=b;
  return tmp;
}

int data_container::read(std::istream& stream)
{
  _has_changed=true;
  std::string buf;
  stream >> buf ;
  if (my_stricmp(buf.c_str(),"points")==0) // ok
  {
    int npts;
    stream >> npts;
//    std::cout << "points " << npts << std::endl;
    points.resize(npts);
    for(int i=0;i<npts;++i)
    {
      point p;
      int szbuf;
      stream >> p.pts[0] >> p.pts[1] >> p.pts[2] >> szbuf;
      if (szbuf)
      {
        char str[szbuf+1];str[szbuf]=0x0;
        stream.read(str,1);
        stream.get(str,szbuf+1);
        std::string s(str);
        p.info=s;
      }
//      std::cout << p.pts << " " << p.info << std::endl;
      points[i]=p;
    }
  }
  else 
   return -1; 
  stream >> buf ;
  if (my_stricmp(buf.c_str(),"proppoints")==0) // ok
  {
    int npts;
    stream >> npts;
//    std::cout << "proppoints " << npts << std::endl;
    proppoints.resize(npts);
    for(int i=0;i<npts;++i)
    {
      int R,G,B,A,Re,Ge,Be,Ae,edgeon,teston;
      double thickness,edgethickness,pointsize;
      std::pair<properties,int> pp;
      stream >> buf >> pp.second >> R >> G >> B >> A >> pp.first.thickness >> pp.first.pointsize >> edgeon>> pp.first.edgethickness>> Re >> Ge >> Be >> Ae >> teston >> pp.first.nb_uid ;
      for (int ii=0;ii<pp.first.nb_uid;++ii) stream >> pp.first.uids[ii];
      pp.first.c=color(R,G,B,A);
      pp.first.edgecolor=color(Re,Ge,Be,Ae);
      pp.first.edgeon=edgeon;
      pp.first.teston=teston;
//      std::cout << "from " << pp.second << std::endl;
      proppoints[i]=pp;
    }
  }
  else 
   return -1; 

  stream >> buf ;
  if (my_stricmp(buf.c_str(),"lines")==0) // ok
  {
    int nl;
    stream >> nl;
//    std::cout << "lines " << nl << std::endl;
    lines.resize(nl);
    for(int i=0;i<nl;++i)
    {
      line l;
      int szbuf;
      stream >> l.pts[0][0] >> l.pts[0][1] >> l.pts[0][2] >> l.pts[1][0] >> l.pts[1][1] >> l.pts[1][2] >> szbuf;
      if (szbuf)
      {
        char str[szbuf+1];str[szbuf]=0x0;
        stream.read(str,1);
        stream.get(str,szbuf+1);
        std::string s(str);
        l.info=s;
      }
//      std::cout << l.pts[0] << " " << l.pts[1] << " " << l.info << std::endl;
      lines[i]=l;
    }
  }
  else 
   return -1; 
  stream >> buf ;
  if (my_stricmp(buf.c_str(),"proplines")==0) // ok
  {
    int npts;
    stream >> npts;
//    std::cout << "proplines " << npts << std::endl;
    proplines.resize(npts);
    for(int i=0;i<npts;++i)
    {
      int R,G,B,A,Re,Ge,Be,Ae,edgeon,teston;
      double thickness,edgethickness,pointsize;
      std::pair<properties,int> pp;
      stream >> buf >> pp.second >> R >> G >> B >> A >> pp.first.thickness >> pp.first.pointsize >> edgeon>> pp.first.edgethickness>> Re >> Ge >> Be >> Ae >> teston >> pp.first.nb_uid ;
      for (int ii=0;ii<pp.first.nb_uid;++ii) stream >> pp.first.uids[ii];
      pp.first.c=color(R,G,B,A);
      pp.first.edgecolor=color(Re,Ge,Be,Ae);
      pp.first.edgeon=edgeon;
      pp.first.teston=teston;
//      std::cout << "from " << pp.second << std::endl;
      proplines[i]=pp;
    }
  }
  else
   return -1;
  
  
  stream >> buf ;
  if (my_stricmp(buf.c_str(),"triangles")==0) // ok
  {
    int nt;
    stream >> nt;
//    std::cout << "triangles " << nt << std::endl;
    triangles.resize(nt);
    for(int i=0;i<nt;++i)
    {
      triangle t;
      int szbuf;
      stream >> t.pts[0][0] >> t.pts[0][1] >> t.pts[0][2] >> t.pts[1][0] >> t.pts[1][1] >> t.pts[1][2] >>  t.pts[2][0] >> t.pts[2][1] >> t.pts[2][2] >>szbuf;
      if (szbuf)
      {
        char str[szbuf+1];str[szbuf]=0x0;
        stream.read(str,1);
        stream.get(str,szbuf+1);
        std::string s(str);
        t.info=s;
      }
//      std::cout << t.pts[0] << " " << t.pts[1] << " " << t.pts[2] << " " << t.info << std::endl;
      triangles[i]=t;
    }
  }
  else 
   return -1; 
  stream >> buf ;
  if (my_stricmp(buf.c_str(),"proptriangles")==0) // ok
  {
    int npts;
    stream >> npts;
//    std::cout << "proptriangles " << npts << std::endl;
    proptriangles.resize(npts);
    for(int i=0;i<npts;++i)
    {
      int R,G,B,A,Re,Ge,Be,Ae,edgeon,teston;
      double thickness,edgethickness,pointsize;
      std::pair<properties,int> pp;
      stream >> buf >> pp.second >> R >> G >> B >> A >> pp.first.thickness >> pp.first.pointsize >> edgeon>> pp.first.edgethickness>> Re >> Ge >> Be >> Ae >> teston >> pp.first.nb_uid ;
      for (int ii=0;ii<pp.first.nb_uid;++ii) stream >> pp.first.uids[ii];
      pp.first.c=color(R,G,B,A);
      pp.first.edgecolor=color(Re,Ge,Be,Ae);
      pp.first.edgeon=edgeon;
      pp.first.teston=teston;
//      std::cout << "from " << pp.second << std::endl;
      proptriangles[i]=pp;
    }
  }
  else 
   return -1; 
  stream >> buf ;
  if (my_stricmp(buf.c_str(),"quads")==0) // ok
  {
    int nt;
    stream >> nt;
//    std::cout << "quads " << nt << std::endl;
    quads.resize(nt);
    for(int i=0;i<nt;++i)
    {
      quad t;
      int szbuf;
      stream >> t.pts[0][0] >> t.pts[0][1] >> t.pts[0][2] >> t.pts[1][0] >> t.pts[1][1] >> t.pts[1][2] >>  t.pts[2][0] >> t.pts[2][1] >> t.pts[2][2] >>  t.pts[3][0] >> t.pts[3][1] >> t.pts[3][2] >>szbuf;
      if (szbuf)
      {
        char str[szbuf+1];str[szbuf]=0x0;
        stream.read(str,1);
        stream.get(str,szbuf+1);
        std::string s(str);
        t.info=s;
      }
//      std::cout << t.pts[0] << " " << t.pts[1] << " " << t.pts[2] << " " << t.pts[3] << " " << t.info << std::endl;
      quads[i]=t;
    }
  }
  else 
   return -1; 
  stream >> buf ;
  if (my_stricmp(buf.c_str(),"propquads")==0) // ok
  {
    int npts;
    stream >> npts;
//    std::cout << "propquads " << npts << std::endl;
    propquads.resize(npts);
    for(int i=0;i<npts;++i)
    {
      int R,G,B,A,Re,Ge,Be,Ae,edgeon,teston;
      double thickness,edgethickness,pointsize;
      std::pair<properties,int> pp;
      stream >> buf >> pp.second >> R >> G >> B >> A >> pp.first.thickness >> pp.first.pointsize >> edgeon>> pp.first.edgethickness>> Re >> Ge >> Be >> Ae >> teston >> pp.first.nb_uid ;
      for (int ii=0;ii<pp.first.nb_uid;++ii) stream >> pp.first.uids[ii];
      pp.first.c=color(R,G,B,A);
      pp.first.edgecolor=color(Re,Ge,Be,Ae);
      pp.first.edgeon=edgeon;
      pp.first.teston=teston;
//      std::cout << "from " << pp.second << std::endl;
      propquads[i]=pp;
    }
  }
  else 
   return -1;
  for(int i=0;i<3;++i)
  {
    int n,nb;
    stream >> buf >> n >> nb;
//    std::cout << "texts " << n << " " << nb << std::endl;
    if ((my_stricmp(buf.c_str(),"texts")==0)&&(n==i)) // ok
    {
      texts[i].resize(nb);
      for(int p=0;p< nb;++p)
      {
        point pt;int R,G,B,A,szbuf;
        stream >>  pt.pts[0] >> pt.pts[1] >> pt.pts[2]  >> R >> G >> B >> A >> szbuf;
        if (szbuf)
        {
          char str[szbuf+1];str[szbuf]=0x0;
          stream.read(str,1);
          stream.get(str,szbuf+1);
          std::string s(str);
          pt.info=s;
        }
//        std::cout << pt.pts << " " << pt.info << std::endl;
        texts[i][p]=std::pair<point,color>(pt,color(R,G,B,A));
      }
    }
    else return -1;
  }
  return 0;
}


void data_container::unhide() const
{
  for(int p=0;p< points.size();++p)
  {
    points[p].drawable=true;
  }
  for(int p=0;p< lines.size();++p)
  {
    lines[p].drawable=true;
  }
  for(int p=0;p< triangles.size();++p)
  {
    triangles[p].drawable=true;
  }
  for(int p=0;p< quads.size();++p)
  {
    quads[p].drawable=true;
  }
}


void data_container::print(std::ostream& stream) const
{
  stream << "points " << points.size() << std::endl;
  for(int p=0;p< points.size();++p)
  {
    stream << points[p].pts << " " << points[p].info.length() << " " << points[p].info << std::endl;
  }
  stream << "proppoints " << proppoints.size() << std::endl;
  for(int p=0;p< proppoints.size();++p)
  {
    stream << "from " << proppoints[p].second << std::endl;
    properties pr=proppoints[p].first;
    stream << (int)pr.c.R  << " " << (int)pr.c.G << " " <<(int) pr.c.B << " " << (int)pr.c.A << " " ;
    stream << pr.thickness << " " << pr.pointsize << " " << pr.edgeon << " " << pr.edgethickness << " ";
    stream << (int)pr.edgecolor.R << " " <<(int)pr.edgecolor.G << " " <<(int)pr.edgecolor.B << " " <<(int)pr.edgecolor.A << " ";
    stream << pr.teston << " " << pr.nb_uid << " ";
    for (int i=0;i<pr.nb_uid;++i) stream << pr.uids[i] << " "; 
    stream << std::endl;
  }

  stream << "lines " << lines.size() << std::endl;
  for(int p=0;p< lines.size();++p)
  {
    stream << lines[p].pts[0] << " " << lines[p].pts[1]<< " " << lines[p].info.length() << " " << lines[p].info << std::endl;
  }
  stream << "proplines " << proplines.size() << std::endl;
  for(int p=0;p< proplines.size();++p)
  {
    stream << "from " << proplines[p].second << std::endl;
    properties pr=proplines[p].first;
    stream << (int)pr.c.R  << " " << (int)pr.c.G << " " <<(int) pr.c.B << " " << (int)pr.c.A << " " ;
    stream << pr.thickness << " " << pr.pointsize << " " << pr.edgeon << " " << pr.edgethickness << " ";
    stream << (int)pr.edgecolor.R << " " <<(int)pr.edgecolor.G << " " <<(int)pr.edgecolor.B << " " <<(int)pr.edgecolor.A << " ";
    stream << pr.teston << " " << pr.nb_uid << " ";
    for (int i=0;i<pr.nb_uid;++i) stream << pr.uids[i] << " "; 
    stream << std::endl;
  }
  
  stream << "triangles " << triangles.size() << std::endl;
  for(int p=0;p< triangles.size();++p)
  {
    stream << triangles[p].pts[0] << " " << triangles[p].pts[1] << " " << triangles[p].pts[2]<< " " << triangles[p].info.length() <<" " << triangles[p].info << std::endl;
  }
  stream << "proptriangles " << proptriangles.size() << std::endl;
  for(int p=0;p< proptriangles.size();++p)
  {
    stream << "from " << proptriangles[p].second << std::endl;
    properties pr=proptriangles[p].first;
    stream << (int)pr.c.R  << " " << (int)pr.c.G << " " <<(int) pr.c.B << " " << (int)pr.c.A << " " ;
    stream << pr.thickness << " " << pr.pointsize << " " << pr.edgeon << " " << pr.edgethickness << " ";
    stream << (int)pr.edgecolor.R << " " <<(int)pr.edgecolor.G << " " <<(int)pr.edgecolor.B << " " <<(int)pr.edgecolor.A << " ";
    stream << pr.teston << " " << pr.nb_uid << " ";
    for (int i=0;i<pr.nb_uid;++i) stream << pr.uids[i] << " "; 
    stream << std::endl;
  }
  
  stream << "quads " << quads.size() << std::endl;
  for(int p=0;p< quads.size();++p)
  {
    stream << quads[p].pts[0] << " " << quads[p].pts[1] << " " << quads[p].pts[2]<< " " << quads[p].pts[3]<< " "<< quads[p].info.length() << " " << quads[p].info << std::endl;
  }
  stream << "propquads " << propquads.size() << std::endl;
  for(int p=0;p< propquads.size();++p)
  {
    stream << "from " << propquads[p].second << std::endl;
    properties pr=propquads[p].first;
    stream << (int)pr.c.R  << " " << (int)pr.c.G << " " <<(int) pr.c.B << " " << (int)pr.c.A << " " ;
    stream << pr.thickness << " " << pr.pointsize << " " << pr.edgeon << " " << pr.edgethickness << " ";
    stream << (int)pr.edgecolor.R << " " <<(int)pr.edgecolor.G << " " <<(int)pr.edgecolor.B << " " <<(int)pr.edgecolor.A << " ";
    stream << pr.teston << " " << pr.nb_uid << " ";
    for (int i=0;i<pr.nb_uid;++i) stream << pr.uids[i] << " "; 
    stream << std::endl;
  }
  for(int i=0;i<3;++i)
  {
    stream << "texts " << i << " " << texts[i].size() << std::endl;
    for(int p=0;p< texts[i].size();++p)
    {
      stream << texts[i][p].first.pts << " " << (int) texts[i][p].second.R << " "  << (int) texts[i][p].second.G << " " << (int) texts[i][p].second.B << " " << (int) texts[i][p].second.A<< " "  << texts[i][p].first.info.length() << " "  << texts[i][p].first.info << std::endl;
    }
  }
}

data_container::data_container(void)
{ 
  setcolorpoints(color());
  setcolorlines(color());
  setcolortriangles(color());
  setcolorquads(color());
  _has_changed=false;
}

data_container::data_container(const data_container &other)
{ 
  merge(other);
}

void data_container::merge(const data_container &other)
{ 
  int size1,propsize1;
  _has_changed=true;
  size1=points.size();
  points.reserve(points.size() + other.points.size() );
  points.insert(points.end(), other.points.begin(), other.points.end());
  proppoints.reserve(proppoints.size() + other.proppoints.size() );
  propsize1=proppoints.size();
  proppoints.insert(proppoints.end(), other.proppoints.begin(), other.proppoints.end());
  if (size1)
  for (int i=propsize1;i<proppoints.size();++i) // adjust links to properties!
    proppoints[i].second+=size1;

  size1=lines.size();
  lines.reserve(lines.size() + other.lines.size() );
  lines.insert(lines.end(), other.lines.begin(), other.lines.end());
  proplines.reserve(proplines.size() + other.proplines.size() );
  propsize1=proplines.size();
  proplines.insert(proplines.end(), other.proplines.begin(), other.proplines.end());
  if (size1)
  for (int i=propsize1;i<proplines.size();++i) // adjust links to properties!
    proplines[i].second+=size1;
  
  
  size1=triangles.size();
  triangles.reserve(triangles.size() + other.triangles.size() );
  triangles.insert(triangles.end(), other.triangles.begin(), other.triangles.end());
  proptriangles.reserve(proptriangles.size() + other.proptriangles.size() );
  propsize1=proptriangles.size();
  proptriangles.insert(proptriangles.end(), other.proptriangles.begin(), other.proptriangles.end());
  if (size1)
  for (int i=propsize1;i<proptriangles.size();++i) // adjust links to properties!
    proptriangles[i].second+=size1;

  size1=quads.size();
  quads.reserve(quads.size() + other.quads.size() );
  quads.insert(quads.end(), other.quads.begin(), other.quads.end());
  propquads.reserve(propquads.size() + other.propquads.size() );
  propsize1=propquads.size();
  propquads.insert(propquads.end(), other.propquads.begin(), other.propquads.end());
  if (size1)
  for (int i=propsize1;i<propquads.size();++i) // adjust links to properties!
    propquads[i].second+=size1;

  for (int i=0;i<3;++i)
  {
    texts[i].reserve(texts[i].size() + other.texts[i].size() );
    texts[i].insert(texts[i].end(), other.texts[i].begin(), other.texts[i].end());
  }
}


void data_container::clear(void)
{
  _has_changed=true;
  points.clear();
  lines.clear();
  triangles.clear();
  quads.clear();
  proppoints.clear();
  proplines.clear();
  proptriangles.clear();
  propquads.clear();
  setcolorpoints(color());
  setcolorlines(color());
  setcolortriangles(color());
  setcolorquads(color());
}

const color& data_container::getcolorpoints(int i) const 
{ 
  return proppoints[i].first.c;
}

const color& data_container::getcolorlines(int i) const
{
  return proplines[i].first.c;
}

const color& data_container::getcolortriangles(int i) const
{
  return proptriangles[i].first.c;
}

const color& data_container::getcolorquads(int i) const
{
  return propquads[i].first.c;
}

const properties& data_container::getproppoints(int i) const 
{ 
  return proppoints[i].first;
}

const properties& data_container::getproplines(int i) const
{
  return proplines[i].first;
}

const properties& data_container::getproptriangles(int i) const
{
  return proptriangles[i].first;
}

const properties& data_container::getpropquads(int i) const
{
  return propquads[i].first;
}


const properties data_container::getproppoints() const 
{ 
  if (getnumproppoints()!=0)
  return proppoints[getnumproppoints()-1].first;
  else
    return properties();
}

const properties data_container::getproplines() const
{
  if (getnumproplines()!=0)
  return proplines[getnumproplines()-1].first;
  else
    return properties();
}

const properties data_container::getproptriangles() const
{
  if (getnumproptriangles()!=0)
  return proptriangles[getnumproptriangles()-1].first;
  else
    return properties();
}

const properties data_container::getpropquads() const
{
  if (getnumpropquads()!=0)
  return propquads[getnumpropquads()-1].first;
  else
    return properties();
}




int data_container::getindexpoints(int i) const 
{ 
  return proppoints[i].second;
}

int data_container::getindexlines(int i) const
{
  return proplines[i].second;
}

int data_container::getindextriangles(int i) const
{
  return proptriangles[i].second;
}

int data_container::getindexquads(int i) const
{
  return propquads[i].second;
}

int data_container::getnumproppoints(void) const
{
  return proppoints.size();
}

int data_container::getnumproplines(void) const
{
  return proplines.size();
}

int data_container::getnumproptriangles(void) const
{
  return proptriangles.size(); 
}

int data_container::getnumpropquads(void) const
{
  return propquads.size();
}

void data_container::setcolorpoints(color c)
{
  properties p=getproppoints();
  p.c=c;
  setproppoints(p);
}

void data_container::setcolorlines(color c)
{
  properties p=getproplines();
  p.c=c;
  setproplines(p);
}

void data_container::setcolortriangles(color c)
{
  properties p=getproptriangles();
  p.c=c;
  setproptriangles(p);
}

void data_container::setcolorquads(color c)
{
  properties p=getpropquads();
  p.c=c;
  setpropquads(p);
}

void data_container::setproppoints(properties p)
{
  proppoints.push_back(std::make_pair(p,points.size()));
}

void data_container::setproplines(properties p)
{
  proplines.push_back(std::make_pair(p,lines.size()));
}


void data_container::setproptriangles(properties p)
{
  proptriangles.push_back(std::make_pair(p,triangles.size()));
}

void data_container::setpropquads(properties p)
{
  propquads.push_back(std::make_pair(p,quads.size()));
}

void data_container::setpropall(properties p)
{
  proppoints.push_back(std::make_pair(p,points.size()));
  proplines.push_back(std::make_pair(p,lines.size()));
  proptriangles.push_back(std::make_pair(p,triangles.size()));
  propquads.push_back(std::make_pair(p,quads.size()));
}

int data_container::add_point(const point& src)
{
  _has_changed=true;
  points.push_back(src);
  return points.size()-1;
}

int data_container::add_point(const npoint3& src)
{
  _has_changed=true;
  point p;p.pts=src;
  points.push_back(p);
  return points.size()-1;
}

int data_container::add_line(const line& src)
{
  _has_changed=true;
  lines.push_back(src);
  return lines.size()-1;
}

int data_container::add_triangle(const triangle& src)
{
  _has_changed=true;
  triangles.push_back(src);
  return triangles.size()-1;
}

int data_container::add_quad(const quad& src)
{
  _has_changed=true;
  quads.push_back(src);
  return quads.size()-1;
}

int data_container::add_text(int num,const std::pair<point,color>& src)
{
  _has_changed=true;
  texts[num].push_back(src);
  return texts[num].size()-1;
}
int data_container::nb_points(void) const
{
  return points.size();
}

int data_container::nb_lines(void) const
{
  return lines.size();
}

int data_container::nb_triangles(void) const
{
  return triangles.size();
}

int data_container::nb_quads(void) const
{
  return quads.size();
}

int data_container::nb_texts(int num) const
{
  return texts[num].size();
}

const point& data_container::get_point(int i) const
{
  return points[i];
}

const line& data_container::get_line(int i) const
{
  return lines[i];
}

const triangle& data_container::get_triangle(int i) const
{
  return triangles[i];
}

const quad& data_container::get_quad(int i) const
{
  return quads[i];
}
const point& data_container::get_text(int num,int i) const
{
  return texts[num][i].first;
}
const color& data_container::get_text_color(int num,int i) const
{
  return texts[num][i].second;
}

point& data_container::get_point(int i)
{
  return points[i];
}

line& data_container::get_line(int i)
{
  return lines[i];
}

triangle& data_container::get_triangle(int i)
{
  return triangles[i];
}

quad& data_container::get_quad(int i)
{
  return quads[i];
}
point& data_container::get_text(int num,int i)
{
  return texts[num][i].first;
}
color& data_container::get_text_color(int num,int i)
{
  return texts[num][i].second;
}


bool is_little_endian(void)
{
  volatile uint32_t i=0x01234567;
  // return 0 for big endian, 1 for little endian.
  return (*((uint8_t*)(&i))) == 0x67;
}

float swapfloat(char b[4])
{
  float f;
  char *b1=(char *) &f;
  b1[0]=b[3];
  b1[1]=b[2];
  b1[2]=b[1];
  b1[3]=b[0]; 
  return(f);
}

uint32_t swaplong(char b[4])
{
  uint32_t l;
  char *b1=(char *) &l;
  b1[0]=b[3];
  b1[1]=b[2];
  b1[2]=b[1];
  b1[3]=b[0]; 
  return(l);
}

uint16_t swapint(char b[2])
{
  uint16_t i;
  char *b1=(char *) &i;
  b1[0]=b[1];
  b1[1]=b[0];
  return(i);
}

int my_stricmp ( const char *s1, const char*s2 )
{
  int c1, c2;
  int cmp = 0;

  if (s1 && s2)
  for (;;)
  {
    c1 = *s1++;
    c2 = *s2++;
    if (c1 && c2)
    {
      c1 = tolower(c1)&0xFF; // 8 bits
      c2 = tolower(c2)&0xFF; // only
      if (c1 < c2)
      {
        cmp = -1;
        break;
      }
      else if (c1 > c2)
      {
        cmp = 1;
        break;
      }
    }
    else
    {
      if (c1)
      cmp = 1;
      else if (c2)
      cmp = -1;
      break;
    }
  }
  return cmp;
}
