////////////////////////////////////////////////////////////////////
//
// $Id: SMFSLIO.cxx 2021/06/05 13:59:59 kanai Exp $
//
// Copyright (c) 2021 Takashi Kanai
// Released under the MIT license
//
////////////////////////////////////////////////////////////////////

#include "envDep.h"

#include <iostream>
#include <fstream>
#include <string>
#include <list>
#include <sstream>
using namespace std;

#include "VertexL.hxx"
#include "HalfedgeL.hxx"
#include "NormalL.hxx"
#include "TexcoordL.hxx"
#include "FaceL.hxx"
#include "BLoopL.hxx"

#include "tokenizer.h"

#include "SMFSLIO.hxx"

bool SMFSLIO::inputFromFile( const char* const filename )
{
  //
  // file open
  //
  std::ifstream ifs; ifs.open( filename );
  if ( !ifs.is_open() )
    {
      std::cerr << "Cannot open " << filename << std::endl;
      return false;
    }

  // for refering vertex pointer
  std::vector<VertexL*>   vertex_p;
  std::vector<NormalL*>   normal_p;
  std::vector<TexcoordL*> tcoord_p;
  std::vector<HalfedgeL*> hedge_p;
  int v_count = 0;
  int f_count = 0;
  //
  // parse smf
  //
  std::string cline;
  while ( getline(ifs, cline, '\n') )
    {
      // comment
      if ( mycomment(cline[0]) ) continue;

      // read vertices
      else if ( cline.find("v") != std::string::npos )
        {
	  std::istringstream isstr( cline );

	  // "v"
	  std::string str; isstr >> str;
	  double x, y, z;
	  isstr >> x; isstr >> y; isstr >> z;
	  Point3d p( x, y, z );

	  VertexL* vt = mesh().addVertex( p );
	  vertex_p.push_back( vt );
// 	  if ( v_count % 10000 == 0 ) 
// 	    cout << "v " << v_count << endl;
	  ++v_count;
        }

      // read normals
      else if ( cline.find("n") != std::string::npos )
        {
	  std::istringstream isstr( cline );
	  
	  // "n"
	  std::string str; isstr >> str;
	  double x, y, z;
	  isstr >> x; isstr >> y; isstr >> z;
	  Vector3d p( x, y, z );

	  NormalL* nm = mesh().addNormal( p );
	  normal_p.push_back( nm );
        }

      // read texture coordinates
      else if ( cline.find("r") != std::string::npos )
        {
	  std::istringstream isstr( cline );
	  
	  // "r"
	  std::string str; isstr >> str;
	  double x, y, z;
	  isstr >> x; isstr >> y;
	  if ( isstr ) isstr >> z;
	  else z=.0f;
	  Point3d p( x, y, z );

	  TexcoordL* tc = mesh().addTexcoord( p );
	  tcoord_p.push_back( tc );
        }

      // read faces
      else if ( cline.find("f") != std::string::npos )
        {
	  std::istringstream isstr( cline );

	  // "f"
	  std::string str; isstr >> str;

	  FaceL* fc = mesh().addFace();
	  while ( isstr >> str )
	    {
	      tokenizer tok( str, "/" );

	      // vertex
	      std::string val = tok.next();
//               cout << "v " << val << " ";
	      int id; 
	      std::istringstream ai(val); ai >> id;
	      HalfedgeL* he = mesh().addHalfedge( fc, vertex_p[id-1], NULL );
	      hedge_p.push_back( he );

	      // normal
	      if ( !(mesh().normals().empty()) )
		{
		  val = tok.next();
//                   cout << "n " << val << " ";
		  std::istringstream ai( val ); ai >> id;
		  he->setNormal( normal_p[id-1] );
		}
	      // texcoord
	      if ( !(mesh().texcoords().empty()) )
		{
		  val = tok.next();
//                   cout << "t " << val << " ";
		  std::istringstream ai( val ); ai >> id;
		  he->setTexcoord( tcoord_p[id-1] );
		}
//               cout << endl;
	      
	    }
	  
	  fc->calcNormal();

// 	  if ( f_count % 10000 == 0 ) 
// 	    cout << "f " << f_count << endl;
	  ++f_count;
        }
      
      // read boundary loop
      else if ( cline.find("b") != std::string::npos )
	{
	  std::istringstream isstr( cline );

	  // "b"
	  std::string str; isstr >> str;
	  
	  BLoopL* bl = mesh().addBLoop();
	  int id;
	  while ( isstr >> id )
	    {
              if ( id > 0 )
                {
                  bl->addIsCorner( true );
                }
              else
                {
                  bl->addIsCorner( false ); 
                  id *= -1; // reverse 
                }
              bl->addVertex( vertex_p[id-1] );
	    }
	  std::cout << "b: " << bl->vertices().size() << " vertices."
		    << std::endl;
	}
      else if ( cline.find("m") != std::string::npos )
        {
	  std::istringstream isstr( cline );
	  
	  // "r"
	  std::string str; 
	  isstr >> str;
	  int id0, id1;
	  isstr >> id0;
	  isstr >> id1;
//  	  cout << id0 << " " << id1 << endl;
	  if ( (id0 != -1) && (id1 != -1) )
	    {
	      HalfedgeL* he0 = hedge_p[id0-1];
	      HalfedgeL* he1 = hedge_p[id1-1];
	      if ( he0->mate() == NULL )
		{
		  he0->setMate( he1 );
		  he1->setMate( he0 );
		}
	      he0->vertex()->setHalfedge( he0 );
	      he1->vertex()->setHalfedge( he1 );
	    }
	  else if ( id0 != -1 )
	    {
	      HalfedgeL* he0 = hedge_p[id0-1];
	      he0->vertex()->setHalfedge( he0 );
	    }
	  else if ( id1 != -1 )
	    {
	      HalfedgeL* he1 = hedge_p[id1-1];
	      he1->vertex()->setHalfedge( he1 );
	    }
        }

      // read faces

      
    } // while

  ifs.close();

  foreach ( std::list<VertexL*>, mesh().vertices(), vt )
    {
      (*vt)->reset_halfedge();
    }

  mesh().setConnectivity( true );
  mesh().printInfo();
  
  return true;
}

bool SMFSLIO::outputToFile( const char* const filename )
{
  mesh().printInfo();

  std::ofstream ofs( filename ); if ( !ofs ) return false;

  // header
  int vn = mesh().vertices().size();
  int tn = mesh().texcoords().size();
  int nn = mesh().normals().size();
  int fn = mesh().faces().size();
  
  ofs << "#$SMF 1.0" << std::endl;
  ofs << "#$vertices " << vn << std::endl;
  if ( nn && isSaveNormal() )
    ofs << "#$normals " << nn << std::endl;
  if ( tn && isSaveTexcoord() )
    ofs << "#$texcoords " << tn << std::endl;
  ofs << "#$faces " << fn << std::endl;
  ofs << "#" << std::endl;

  if ( vn )
    {
      int id = 1;
      foreach ( std::list<VertexL*>, mesh().vertices(), vt )
        {
	  Point3d p( (*vt)->point() );
	  if ( mesh().isNormalized() )
	    {
	      p.scale( mesh().maxLength() );
	      p += mesh().center();
	    }
	  ofs << "v\t"
	      << p.x << " "
	      << p.y << " "
	      << p.z << std::endl;
          (*vt)->setID(id); id++;
        }
    }

  //int nn = mesh().normals().size();
  if ( nn && isSaveNormal() )
    {
      int id = 1;
      foreach ( std::list<NormalL*>, mesh().normals(), nm )
        {
          ofs << "n\t"
	      << (*nm)->point().x << " "
	      << (*nm)->point().y << " "
	      << (*nm)->point().z << std::endl;
          (*nm)->setID(id); id++;
        }
    }

  //int tn = mesh().texcoords().size();
  if ( tn && isSaveTexcoord() )
    {
      int id = 1;
      foreach ( std::list<TexcoordL*>, mesh().texcoords(), tc )
        {
          ofs << "r\t"
	      << (*tc)->point().x << " "
	      << (*tc)->point().y << " "
	      << (*tc)->point().z << std::endl;
          (*tc)->setID(id); id++;
        }
    }

  if ( fn )
    {
      foreach ( std::list<FaceL*>, mesh().faces(), fc )
        {
          ofs << "f\t" ;
          foreach ( std::list<HalfedgeL*>, (*fc)->halfedges(), he )
            {
              ofs << (*he)->vertex()->id();
              if ( (*he)->normal() && isSaveNormal() )
                ofs << "/" << (*he)->normal()->id();
              if ( (*he)->texcoord() && isSaveTexcoord() )
                ofs << "/" << (*he)->texcoord()->id();
              ofs << " " ;
            }
          ofs << std::endl;
        }

      if ( mesh().isConnectivity() )
        {
          foreach ( std::list<FaceL*>, mesh().faces(), fc )
            {
              foreach ( std::list<HalfedgeL*>,(*fc)->halfedges(), he )
                {
          // foreach ( std::list<HalfedgeL*>, mesh().halfedges(), he )
          //   {
                  if ( (*he)->mate() != NULL )
                    {
                      ofs << "m\t" << (*he)->id()+1 << " " << (*he)->mate()->id()+1 << std::endl;
                    }
        // }
                }
            }
        }
    }

  // save bloop
  if ( isSaveBLoop() && mesh().bloops().size() )
    {
      BLoopL* bl = *(mesh().bloops().begin());

      ofs << "b\t" ;
      for ( unsigned int i = 0; i < bl->vertices().size(); ++i )
        {
          int id = bl->vertex(i)->id();

          if ( !(bl->isCorner(i)) ) id *= -1; // reverse

          ofs << id << " ";

        }
      ofs << std::endl;
    }

  ofs.close();

  // reset numbers

  if ( vn )
    {
      int id = 0;
      foreach ( std::list<VertexL*>, mesh().vertices(), vt )
        {
          (*vt)->setID(id); id++;
        }
    }
  if ( nn && isSaveNormal() )
    {
      int id = 0;
      foreach ( std::list<NormalL*>, mesh().normals(), nm )
        {
          (*nm)->setID(id); id++;
        }
    }
  if ( tn && isSaveTexcoord() )
    {
      int id = 1;
      foreach ( std::list<TexcoordL*>, mesh().texcoords(), tc )
        {
          (*tc)->setID(id); id++;
        }
    }

  return true;
}

