#include "./funcs.hpp"

int deal_with_seen(mesh_geometry& targetMesh, vertex* seenVert, unsigned int* checkIndices, const unsigned int indicesCount, int material_id)
{
  //has already been seen
  bool same= true;
  for(uint32 si=1; si< indicesCount; si++){
    if(seenVert->indices[si] != checkIndices[si]) { same= false; break; }
  }

  if(same){
    targetMesh.indices.push_back(seenVert->indices[0]);
    return seenVert->indices[0];
  }
  else{ //found twin (same vertex with different material attributes)
  
    if(seenVert->twin_index != -1){
      return deal_with_seen(targetMesh, &targetMesh.vertices[seenVert->twin_index], checkIndices, indicesCount, material_id);
    }
    else {
      targetMesh.vertices.push_back(vertex());
      unsigned int ps_= targetMesh.vertices.size()-1;
      vertex& twv= targetMesh.vertices[ps_];
      
      //assign new vertex properties
      twv.material_id= material_id;
      twv.indices.resize(indicesCount);
      for(uint32 si= 0; si< indicesCount; si++) twv.indices[si]= checkIndices[si];

      twv.seen= true;
      twv.twin_index= -1;

      seenVert->twin_index= ps_;
      targetMesh.indices.push_back(ps_);

      return ps_;
    }
  }
};

void construct_mesh_geometries(std::vector<mesh_geometry>& meshes)
{
  for(uint32 mi= 0; mi< meshes.size(); mi++)
  {
    construct_mesh_geometry(meshes[mi]);
  }
}

void construct_mesh_geometry(mesh_geometry& mesh)
{
  raw_meshdata& rawMeshData= mesh.raw;

  //material group vertices to geom mesh vertices
  for(uint32 mgi= 0; mgi< rawMeshData.prim_groups.size(); mgi++)
  {
    raw_primitives_group& primGroup= rawMeshData.prim_groups[mgi];
    unsigned int numSources= primGroup.inputs_count;

    //TODO: Remove dd stuff, if possible!
    num1D_data& dd= rawMeshData.sources[0];
    int unitSize= (dd.gltype== GL_FLOAT)? sizeof(float): (dd.gltype== GL_INT)? sizeof(int): 1;

    //push material group vertices
    for(uint32 j= 0; j< (dd.size/unitSize)/dd.stride; j++){

      mesh.vertices.push_back(vertex());
      vertex& c= mesh.vertices.back();

      //init vertex indices
      c.indices.resize(numSources);
      for(uint32 si= 0; si< numSources; si++){
        c.indices[si]= 0;
      }

      c.material_id = primGroup.material_id;
      c.indices[dd.semantic]= j;
      c.seen= false;
      c.twin_index= -1;
    }
  }
  
  //material group indices to geom mesh indices
  for(uint32 mgi= 0; mgi< rawMeshData.prim_groups.size(); mgi++)
  {
    raw_primitives_group& primGroup= rawMeshData.prim_groups[mgi];
    unsigned int numSources= primGroup.inputs_count;        

    //scan through the indices and fill in the details
    unsigned int extrIndices[numSources];
    for(uint32 y= 0; y< primGroup.indices_count; y++){

      for(uint32 si= 0; si< numSources; si++)
      {
        extrIndices[si]= primGroup.indices[y*primGroup.inputs_count+si];
      }

      int posi= extrIndices[0];//posi;
      int ps= posi;
      if(mesh.vertices[posi].seen == false)
      {
        mesh.indices.push_back(posi);
        ps= posi;
        //set ref indices
        for(uint32 si= 0; si< numSources; si++) mesh.vertices[posi].indices[si]= extrIndices[si];
        
        //init vertex properties
        mesh.vertices[posi].seen= true;
        mesh.vertices[posi].material_id= primGroup.material_id;
        mesh.vertices[posi].twin_index= -1;
      }
      else {
        ps= deal_with_seen(mesh, &mesh.vertices[posi], extrIndices, numSources, primGroup.material_id);
      }      
    }
  
    //extract triangles
    for(uint32 y= 0; y< primGroup.indices_count; y+=3){

      int posit[3];
      int k= y;
      for(uint32 m= 0; m< 3; m++)
      {
        for(uint32 si= 0; si< numSources; si++)
          extrIndices[si]= primGroup.indices[k*primGroup.inputs_count+si];

        posit[m]= extrIndices[0];
        k++;
      }

      mesh.triangles.push_back(triangle(posit[0], posit[1], posit[2]));
    }
  }
  
  //extract edges
  std::map<std::pair<int,int>,int> edgesLookupMap;
  for(uint32 ti= 0; ti< mesh.triangles.size(); ti++){

    triangle& tl= mesh.triangles[ti];
    for(uint32 pi=0; pi< 3; pi++)
    {
      int a= tl.posit[pi];
      int b= tl.posit[pi+1>2 ? 0: pi+1];

      int swap=-1;
      if(a>b){ swap= a; a= b; b= swap; }//order
      int edgei= -1;
      auto itt= edgesLookupMap.find(std::pair<int,int>(a,b));
      if(itt != edgesLookupMap.end())
      {
        edgei= itt->second;
      }
      else{
        mesh.edges.push_back(edge(a,b));
        edgei= mesh.edges.size()-1;
        edgesLookupMap.insert(std::pair<std::pair<int,int>,int>(std::pair<int,int>(a,b), edgei));
      }
      tl.edges[pi]= edgei;
    }
  }  
};

void find_mesh(const std::string& meshId, const bindoc& binDoc, int& index)
{
  index= -1;
  for(unsigned int mi= 0; mi< binDoc.meshes.size(); mi++)
  {
    const mesh_geometry& geom= binDoc.meshes[mi];
    std::string geomid(geom.meshId);
    geomid= "#" + geomid;
    if(geomid.compare(meshId)==0) index= mi;
  }
};