//
// This file is part of MorphoGraphX - http://www.MorphoGraphX.org
// Copyright (C) 2012-2015 Richard S. Smith and collaborators.
//
// If you use MorphoGraphX in your work, please cite:
//   http://dx.doi.org/10.7554/eLife.05864
//
// MorphoGraphX is free software, and is licensed under under the terms of the 
// GNU General (GPL) Public License version 2.0, http://www.gnu.org/licenses.
// 
#ifndef MESH_PROCESS_CREATION_HPP
#define MESH_PROCESS_CREATION_HPP

#include <Process.hpp>

#include <Polygonizer.hpp>

namespace mgx
{
  ///\addtogroup MeshProcess
  ///@{
  /**
   * \class MarchingCubeSurface ProcessCreation.hpp <MeshProcessCreation.hpp>
   *
   * Find the surface of a volume defined by an iso-surface on the image
   * intensity using a variation on the Marching Cube method.
   */
  class mgxBase_EXPORT MarchingCubeSurface : public Process, public ImplicitFunction 
  {
  public:
    MarchingCubeSurface(const Process& process) : Process(process) 
    {
		
      setName("Mesh/Creation/Marching Cubes Surface");
      setDesc("Extract surface mesh with marching cubes. A threshold to 0 will interpret the image as binary.");
      setIcon(QIcon(":/images/MarchCubes.png"));  
      addParm("Cube size (µm)","Size for the marching cubes.","5.0");		// 0
      addParm("Threshold","Minimal signal used for surface extraction.","5000");		// 1		
		
		}
  
    bool run()
    {
      if(!checkState().store(STORE_NON_EMPTY).mesh())
        return false;
  
      _stack = currentStack();
      _store = _stack->currentStore();
      Mesh* mesh = currentMesh();
  
      bool result = run(mesh, _store, parm("Cube size (µm)").toFloat(), parm("Threshold").toInt());
      if(result)
        mesh->setShowVertex("Signal");
      if(result and not mesh->showSurface() and not mesh->showMesh())
        mesh->setShowSurface(true);
      return result;
    }
  
    bool run(Mesh* mesh, const Store* store, float cubeSize, int threshold);
 
    bool eval(Point3f p);
  
  private:
    int _threshold;
    const Stack* _stack;
    const Store* _store;
  };
  
  /**
   * \class MarchingCubeSurface ProcessCreation.hpp <MeshProcessCreation.hpp>
   *
   * Find the surface of the segmented volumes using a variation on
   * the Marching Cube method.
   */
  class mgxBase_EXPORT MarchingCube3D : public Process, public ImplicitFunction 
  {
  public:
    MarchingCube3D(const Process& process) : Process(process) 
    {		
      setName("Mesh/Creation/Marching Cubes 3D");
      setDesc("Extract 3D meshes with marching cubes");
      setIcon(QIcon(":/images/MarchCubes3D.png"));
      
      addParm("Cube size (µm)","Size for the marching cubes.","5.0");		// 0
      addParm("Min Voxels","Minimal number of voxels for extracting surface.","0");		// 1
      addParm("Smooth Passes","Smooth Passes.","3");		// 2
      addParm("Label","Extract surface only for this label.","0");		// 3				
	}
  
    bool run()
    {
      if(!checkState().store(STORE_LABEL).mesh())
        return false;
  
      _stack = currentStack();
      _store = _stack->currentStore();
      Mesh* mesh = currentMesh();
  
      bool result = run(mesh, _store, parm("Cube size (µm)").toFloat(), parm("Min Voxels").toInt(), 
                                                parm("Smooth Passes").toInt(), parm("Label").toInt());
      if(result)
        mesh->setShowLabel();
      if(result and not mesh->showSurface() and not currentMesh()->showMesh())
        currentMesh()->setShowSurface(true);
      return result;
    }
  
    bool run(Mesh* mesh, const Store* store, float cubeSize, int minVoxels, 
                                                             int smooth, int singleLabel);
  
    bool eval(Point3f p);
  
  private:
    int _label;
    const Stack* _stack;
    const Store* _store;
  };
  
  /**
   * \class CuttingSurfMesh ProcessCreation.hpp <MeshProcessCreation.hpp>
   *
   * Create a surface matching the current cutting surface, whether it's a
   * plane or a Bezier surface.
   */
  class mgxBase_EXPORT CuttingSurfMesh : public Process 
  {
  public:
    CuttingSurfMesh(const Process& process) : Process(process) 
    {
      setName("Mesh/Creation/Mesh Cutting Surface");
      setDesc("Make mesh from cutting surface");				
	}
  
    bool run()
    {
      if(!checkState().mesh())
        return false;
      return run(currentMesh());
    }
  
    bool run(Mesh* mesh);
  };

  /**
   * \class VoxelFaceMesh ProcessCreation.hpp <MeshProcessCreation.hpp>
   *
   * Create a mesh by extracting the faces from the Voxels.
   */
  class mgxBase_EXPORT VoxelFaceMesh : public Process 
  {
  public:
    VoxelFaceMesh(const Process& process) : Process(process) 
    {		
      setName("Mesh/Creation/Voxel Face Mesh");
      setDesc("Extract a mesh from the faces of the voxels.");				
    }
  
    bool run()
    {
      if(!checkState().mesh().store(STORE_LABEL))
        return false;
      return run(currentStack(), currentStack()->currentStore(), currentMesh());
    }
  
    bool run(Stack *stack, Store *store, Mesh *mesh);
  };


  class mgxBase_EXPORT MeshFromLocalMaxima : public Process {
  public:
    MeshFromLocalMaxima(const Process& process) : Process(process) 
    {
      setName("Mesh/Creation/Mesh From Local Maxima");
      setDesc("Create a Mesh from Local Maxima");

      addParm("Radius (µm)","Radius (µm)","3.0");	
      
    }			
						
    bool run()
    {
      if(!checkState().store(STORE_NON_EMPTY))
        return false;
      return run(currentMesh(), currentStack()->currentStore(), parm("Radius (µm)").toFloat());
    }
  
    bool run(Mesh* mesh, const Store* store, float radius);
  };
  ///@}
}

#endif
