/**============================================================================
 *
 * \file   gmsh_writer.hpp
 * \brief  Declarations for the class GmshWriter.
 *
 * \author Leblanc Christophe.
 * \date   23/02/2016
 * \email cleblancad@gmail.com
 *
 *===========================================================================*/

#ifndef GMSH_WRITER_HPP
#define GMSH_WRITER_HPP

#include <iostream>
#include <string>
#include <vector>
#include <utility>
#include <map>
#include <cstdlib>

namespace marinov {

/** \class GmshWriter
 * \brief Writes mesh data to file in 'geo' format.
 * \WARNING Input meshes must be triangulated.
 */
template<typename Face_decimationT>
class GmshWriter
{
public:
  //
  // Typedefs.
  //

  /// \brief Mesh type.
  typedef typename Face_decimationT::Mesh MeshType;
  typedef typename Face_decimationT::Mesh_properties PropertiesType;

  typedef typename Face_decimationT::Boundary_type BoundaryType;

  //
  // Functions.
  //

  /// \brief Default constructor.
  /// \param[in] fileName output file name.
  /// \param[in] globalMesh mesh of the sample.
  /// \param[in] scale characteristic mesh scale.
  /// \param[in] printPrecision number of decimals to print out floating values.
  /// \param[in] os stream for outputing text in verbose mode.
  GmshWriter(const std::string &fileName, 
             MeshType &globalMesh,
	     double scale, unsigned int printPrecision,
             std::ostream &os = std::cerr);
  
  /// \brief Copy constructor.
  /// \param[in] rhs object to copy.
  GmshWriter(const GmshWriter &rhs);
  
  /// \brief Destructor.
  ~GmshWriter() {}
  
  /// \brief Copy operator.
  /// \param[in] rhs object to copy.
  /// \return Copied object.
  GmshWriter& operator=(const GmshWriter &rhs);

  /// \brief Writes meshes' data to file.
  void Write();
  
  /// \brief Get the verbose mode.
  /// \return True if the verbose mode is set to "on" and false otherwise.
  bool GetVerbose() const
  { return m_Verbose; }

  /// \brief Set the verbose mode.
  /// \param[in] verbose if true set the verbose mode to "on" and "off" otherwise.
  void SetVerbose(const bool verbose)
  { m_Verbose = verbose; }

  /// \brief Gets the output stream for error messages.
  /// \return Stream.
  inline const std::ostream* GetErrorStream() const
  { return m_Os; }

  /// \brief Gets the mesh properties.
  /// \return Mesh properties.
  inline const PropertiesType& get_properties() const
  { return *m_MeshProperties; }

  /// \brief Sets the mesh properties.
  /// \param[in] prop mesh properties.
  inline void set_properties(const PropertiesType &prop)
  { m_MeshProperties = &prop; }

private:
  //
  // Functions.
  //

  /// \brief Helper function for function'Write'.
  /// \param[in] os output stream.
  void WriteHelper(std::ostream &os);

  /// \brief Prints header of Gmsh file.
  /// \param[in] os wanted output stream.
  void PrintHeader(std::ostream &os) const;

  /// \brief Prints vertices for each cell.
  /// \param[in] os wanted output stream.
  void PrintVertices(std::ostream &os);

  /// \brief Prints edges for each cell.
  /// \param[in] os wanted output stream.
  void PrintEdges(std::ostream &os);

  /// \brief Prints faces for each cell.
  /// \param[in] os wanted output stream.
  void PrintFaces(std::ostream &os);

  /// \brief Prints volumes for each cell.
  ///   Prints only the pore volumes (as the global mesh has no volume).
  /// \param[in] os wanted output stream.
  void PrintVolumes(std::ostream &os);

  /// \brief Checks if edges of one face are correctly oriented in the mesh.
  /// If not, the edge tag is put to -1 (1 if ok).
  /// \param[in,out] mesh mesh to consider.
  /// \param[in] fh face handle of the mesh to consider.
  void CheckEdgeOrientations(const MeshType &mesh, const typename MeshType::Face_index &fh);

  //
  // Variables.
  //
  std::string m_FileName;	///< Name of the output file.
  MeshType *m_Mesh;        	///< Mesh of the sample.
  double m_Scale;		///< Characteristic mesh scale.
  unsigned int m_PrintPrecision; ///< Number of decimals to print out floating values.

  /// \brief tag for the verbose mode.
  bool m_Verbose;
   
  /// \brief Output stream for error messages.
  std::ostream* m_Os;

  /// \brief Mesh properties.
  const PropertiesType *m_MeshProperties;

  struct VertexData
  {
    long int num;
  };

  struct EdgeData
  {
    bool m_Visited;
    long int num;
    int tag;
    
    EdgeData(): m_Visited(false) {}
    bool IsVisited() const { return m_Visited; }
    void SetVisited() { m_Visited = true; }
  };

  struct FaceData
  {
    long int num;
  };

  std::map<typename MeshType::Vertex_index, VertexData> m_VertexData;
  std::map<typename MeshType::Edge_index, EdgeData> m_EdgeData;
  std::map<typename MeshType::Face_index, FaceData> m_FaceData;
};

} // end namespace marinov

#include "gmsh_writer.hxx"

#endif // GMSH_WRITER_HPP
