// Copyright (c) 2012  INRIA Sophia-Antipolis (France).
// All rights reserved.
//
// This file is part of CGAL (www.cgal.org).
// You can redistribute it and/or modify it under the terms of the GNU
// General Public License as published by the Free Software Foundation,
// either version 3 of the License, or (at your option) any later version.
//
// Licensees holding a valid commercial license may use this file in
// accordance with the commercial license agreement provided with the software.
//
// This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
// WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
//
// $URL$
// $Id$
// SPDX-License-Identifier: GPL-3.0+
//
// Author(s)     : Clement Jamin

#ifndef CGAL_IO_FILE_MAYA_H
#define CGAL_IO_FILE_MAYA_H

#include <CGAL/license/Mesh_3.h>

#include <CGAL/Hash_handles_with_or_without_timestamps.h>
#include <CGAL/utility.h>

#include <boost/unordered_map.hpp>

#include <iostream>
#include <vector>
#include <string>
#include <sstream>

namespace CGAL {

//-------------------------------------------------------
// IO functions
//-------------------------------------------------------
  
template <class C3T3>
void
output_to_maya(std::ostream& os, 
               const C3T3& c3t3, 
               bool surfaceOnly = true)
{
  typedef typename C3T3::Triangulation Tr;
  typedef typename C3T3::Facets_in_complex_iterator Facet_iterator;
  typedef typename C3T3::Cells_in_complex_iterator Cell_iterator;

  typedef typename Tr::Finite_vertices_iterator Finite_vertices_iterator;
  typedef typename Tr::Vertex_handle Vertex_handle;
  typedef typename Tr::Weighted_point Weighted_point;

  typedef CGAL::Hash_handles_with_or_without_timestamps Hash_fct;

#ifdef CGAL_MESH_3_IO_VERBOSE
  std::cerr << "Output to maya:\n";
#endif
  
  const Tr& tr = c3t3.triangulation();

  //-------------------------------------------------------
  // File output
  //-------------------------------------------------------

  //-------------------------------------------------------
  // Header
  //-------------------------------------------------------
  os << std::setprecision(20);

  os << "//Maya ASCII 2011 scene" << std::endl;
  os << "//Name: testMaya3.ma" << std::endl;
  os << "//Last modified: Wed, Jan 25, 2012 05:54:26 PM" << std::endl;
  os << "//Codeset: 1252" << std::endl;
  os << "requires maya \"2011\";" << std::endl;
  os << "currentUnit -l centimeter -a degree -t film;" << std::endl;
  os << "fileInfo \"application\" \"maya\";" << std::endl;
  os << "fileInfo \"product\" \"Maya 2011\";" << std::endl;
  os << "fileInfo \"version\" \"2011\";" << std::endl;
  os << "fileInfo \"cutIdentifier\" \"201003190014-771504\";" << std::endl;
  os << "fileInfo \"license\" \"education\";" << std::endl;

  std::string name = "Mesh_3";
  os << "createNode mesh -n \"" << name << "Shape\" -p \"" << name << "\";" << std::endl;
  os << "  setAttr -k off \".v\";" << std::endl;
  os << "  setAttr \".uvst[0].uvsn\" -type \"string\" \"map1\";" << std::endl;
  os << "  setAttr \".cuvs\" -type \"string\" \"map1\";" << std::endl;
  os << "  setAttr \".dcol\" yes;" << std::endl;
  os << "  setAttr \".dcc\" -type \"string\" \"Ambient+Diffuse\";" << std::endl;
  
  os << "  connectAttr \"" << name << "Shape.iog\" \":initialShadingGroup.dsm\" -na;\n\n";
    
  //-------------------------------------------------------
  // Colors
  //------------------------------------------------------
  
  /*os << "  setAttr \".ccls\" -type \"string\" \"colorSet\";\n";
  os << "  setAttr \".clst[0].clsn\" -type \"string\" \"colorSet\";\n";
  os << "  setAttr \".clst[0].rprt\" 3;\n";
  os << "  setAttr -s " << 3 << " \".clst[0].clsp[0:" << 3-1 << "]\"" << std::endl;
  os << "    10 50 250" << std::endl;
  os << "    100 250 50" << std::endl;
  os << "    0 200 200" << std::endl;
  os << "  ;\n";*/
  
  //-------------------------------------------------------
  // Vertices
  //------------------------------------------------------
  
  boost::unordered_map<Vertex_handle, int, Hash_fct> V;
  std::stringstream vertices_sstr;
  int num_vertices = 0;
  for( Finite_vertices_iterator vit = tr.finite_vertices_begin();
       vit != tr.finite_vertices_end();
       ++vit)
  {
    if ( (surfaceOnly  && c3t3.in_dimension(vit) <= 2)
      || !surfaceOnly)
    {
      V[vit] = num_vertices++;
      Weighted_point p = tr.point(vit);
      vertices_sstr << "    " << CGAL::to_double(p.x()) << " " << CGAL::to_double(p.y()) << " " << CGAL::to_double(p.z()) << std::endl;
    }
  }
    
  os << "  setAttr -s " << num_vertices << " \".vt[0:" << num_vertices-1 << "]\"" << std::endl;
  os << vertices_sstr.str();
  os << ";\n";

  /*
  // Triangles
  os << "setAttr -s " << QString().setNum(number_of_triangles) << " \".fc[0:" << QString().setNum(number_of_triangles-1) << "]\"  -type \"polyFaces\" \n";
  for (int i=0;i<triangle.size();i=i+3){
    int a=int(triangle[i+0]);
    int b=int(triangle[i+1]);
    int c=int(triangle[i+2]);
    os << "f 3 " << QString().setNum(a) << " " << QString().setNum(b) << " " << QString().setNum(c) << " ";
    if (mat.materialNode==VertexColor){
      os << "mc 0 3 " << QString().setNum(index[i*2]) << " " << QString().setNum(index[i*2+2]) << " " << QString().setNum(index[i*2+4]) << " ";
    }
  }
  os << ";\n\n";*/

  //-------------------------------------------------------
  // Get edges and facets
  //-------------------------------------------------------

  typename C3T3::size_type number_of_triangles = c3t3.number_of_facets_in_complex();
  
  std::stringstream facets_sstr;
  //std::stringstream normals_sstr;

  //normals_sstr << "  setAttr -s " << number_of_triangles*3 << " \".n[0:" << number_of_triangles*3-1 << "]\"  -type \"float3\" \n";

  // Save edges
  typedef std::vector<std::pair<int, int> > EdgeList;
  EdgeList edges;

  // Surface only
  if (surfaceOnly)
  {
    facets_sstr <<  "  setAttr -s " << number_of_triangles 
      << " \".fc[0:" << number_of_triangles-1 << "]\"  -type \"polyFaces\" \n";
    int c = 0;
    for( Facet_iterator fit = c3t3.facets_in_complex_begin();
         fit != c3t3.facets_in_complex_end();
         ++fit, ++c)
    {
      int indices[3];
      //Weighted_point points[3];
      facets_sstr << "    f 3 ";
      for (int j = 0, i = (fit->second + 1) % 4 ; j < 3 ; i = (i+1)%4, ++j)
      {
        const Vertex_handle& vh = fit->first->vertex(i);
        indices[j] = V[vh];
        //points[j] = tr.point(fit->first, i);
      }
    
      // Reverse triangle orientation?
      bool reverse_triangle = 
           (fit->second % 2 == 0 && !c3t3.is_in_complex(fit->first))
        || (fit->second % 2 != 0 && c3t3.is_in_complex(fit->first));
      if (reverse_triangle)
      {
        std::swap(indices[1], indices[2]);
        //std::swap(points[1], points[2]);
      }
      //Kernel::Vector_3 n = cross_product(points[1] - points[0], points[2] - points[0]);
      //n = n / CGAL::sqrt(n*n);
      // Add the normal 3 times
      //normals_sstr << "    " << n.x() << " " << n.y() << " " << n.z() << std::endl;
      //normals_sstr << "    " << n.x() << " " << n.y() << " " << n.z() << std::endl;
      //normals_sstr << "    " << n.x() << " " << n.y() << " " << n.z() << std::endl;

      // 3 edges
      for (int i = 0 ; i < 3 ; ++i)
      {
        std::pair<int, int> edge = std::make_pair(
          (std::min)(indices[i], indices[(i+1)%3]),
          (std::max)(indices[i], indices[(i+1)%3]));
        size_t pos = std::find(edges.begin(), edges.end(), edge) - edges.begin();
        if (pos == edges.size()) // Not found?
        {
          edges.push_back(edge);
        }
        // ith edge of triangle
        facets_sstr << pos << " ";
      }
    
      // 1 triangles
      facets_sstr << std::endl;
      // Colors
      //facets_sstr << "    mc 0 3 " << rand()%3 << " " << rand()%3 << " " << rand()%3 << std::endl;
    }
  }
  // Tetrahedra = 4 facets for each
  else
  {
    facets_sstr <<  "  setAttr -s " << 4*c3t3.number_of_cells_in_complex() 
      << " \".fc[0:" << 4*c3t3.number_of_cells_in_complex()-1 << "]\"  -type \"polyFaces\" \n";
    int c = 0;
    for( Cell_iterator cit = c3t3.cells_in_complex_begin();
         cit != c3t3.cells_in_complex_end();
         ++cit, ++c)
    {
      for (int facet_i = 0 ; facet_i < 4 ; ++facet_i)
      {
        int indices[3];
        //Weighted_point points[3];
        facets_sstr << "    f 3 ";
        for (int j = 0, i = (facet_i + 1) % 4 ; j < 3 ; i = (i+1)%4, ++j)
        {
          const Vertex_handle& vh = cit->vertex(i);
          indices[j] = V[vh];
          //points[j] = tr.point(cit, i);
        }
    
        // Reverse triangle orientation?
        bool reverse_triangle = (facet_i % 2 != 0 && c3t3.is_in_complex(cit, facet_i));
        if (reverse_triangle)
        {
          std::swap(indices[1], indices[2]);
          //std::swap(points[1], points[2]);
        }
        //Kernel::Vector_3 n = cross_product(points[1] - points[0], points[2] - points[0]);
        //n = n / CGAL::sqrt(n*n);
        // Add the normal 3 times
        //normals_sstr << "    " << n.x() << " " << n.y() << " " << n.z() << std::endl;
        //normals_sstr << "    " << n.x() << " " << n.y() << " " << n.z() << std::endl;
        //normals_sstr << "    " << n.x() << " " << n.y() << " " << n.z() << std::endl;

        // 3 edges
        for (int i = 0 ; i < 3 ; ++i)
        {
          std::pair<int, int> edge = std::make_pair(
            (std::min)(indices[i], indices[(i+1)%3]),
            (std::max)(indices[i], indices[(i+1)%3]));
          size_t pos = std::find(edges.begin(), edges.end(), edge) - edges.begin();
          if (pos == edges.size()) // Not found?
          {
            edges.push_back(edge);
          }
          // ith edge of triangle
          facets_sstr << pos << " ";
        }
    
        // 1 triangles
        facets_sstr << std::endl;
        // Colors
        //facets_sstr << "    mc 0 3 " << rand()%3 << " " << rand()%3 << " " << rand()%3 << std::endl;
      }
    }
  }
  facets_sstr << ";\n\n";
  //normals_sstr << ";\n\n";
  
  //-------------------------------------------------------
  // Edges
  //-------------------------------------------------------
  os << "  setAttr -s " << edges.size() << " \".ed[0:" 
     << edges.size() - 1 << "]\"" << std::endl;

  for (EdgeList::const_iterator it = edges.begin(), it_end = edges.end() ; it != it_end ; ++it)
    os << "    " << it->first << " " << it->second << " " << 0 << std::endl;

  os << ";\n";
  
  //-------------------------------------------------------
  // Normals
  //-------------------------------------------------------
  
  //os << normals_sstr.str();

  //-------------------------------------------------------
  // Facets
  //-------------------------------------------------------
  
  os << facets_sstr.str();
  
  //-------------------------------------------------------
  // Tetrahedra
  //-------------------------------------------------------
  /*os << "Tetrahedra" << std::endl
     << c3t3.number_of_cells_in_complex() << std::endl;

  for( Cell_iterator cit = c3t3.cells_in_complex_begin() ;
       cit != c3t3.cells_in_complex_end() ;
       ++cit )
  {
    for (int i=0; i<4; i++)
      os << V[cit->vertex(i)] << " ";

    os << get(cell_pmap, cit) << std::endl;
  }*/

  //-------------------------------------------------------
  // End
  //-------------------------------------------------------

#ifdef CGAL_MESH_3_IO_VERBOSE
  std::cerr << "done.\n";
#endif
} // end output_to_maya(...)

} // end namespace CGAL

#endif // CGAL_IO_FILE_MAYA_H
