//
// This file is part of MorphoGraphX - http://www.MorphoGraphX.org
// Copyright (C) 2012-2016 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_CELL_MESH_HPP
#define MESH_PROCESS_CELL_MESH_HPP

#include <Process.hpp>

#include <MeshProcessSegmentation.hpp>

namespace mgx 
{
  ///\addtogroup MeshProcess
  ///@{
  /**
   * \class FixMeshCorners ProcessCellMesh.hpp <MeshProcessCellMesh.hpp>
   *
   * Fix labelling of cell corners. Depending on the topology, it may be that a
   * triangle between three segmented cells cannot be labeled correctly. This
   * process identifies such triangle and sub-divide them in the hope of
   * changing the topology and solve the problem. This process may need to be
   * called many times to solve all the issues as this is only a heuristic that
   * tends to improve the situation.
   */
  class mgxBase_EXPORT FixMeshCorners : public Process 
  {
  public:
    FixMeshCorners(const Process& process) : Process(process) 
    {
	  setName("Mesh/Cell Mesh/Fix Corners Classic");
	  setDesc("Fix labelling of cell corners.");
	  setIcon(QIcon(":/images/FixCorners.png"));
	  
	  addParm("Auto segmentation?","Re-run watershed automatically at each turn.","Yes",booleanChoice());
	  addParm("Select bad vertices","Select remaining bad vertices at the end. \n"
	  "Helps in deleting bad vertices that cannot be segmented properly.","Yes",booleanChoice());
	  addParm("Max iterations","Maximal number of turns.","5");	
	}
  
    bool run()
    {
      if(!checkState().mesh(MESH_NON_EMPTY))
        return false;
      if(parm("Max iterations").toInt() < 1) {
        setErrorMessage("Number of runs must be at least one.");
        return false;
      }
      return run(currentMesh(), stringToBool(parm("Auto segmentation?")), 
                                  stringToBool(parm("Select bad vertices")), parm("Max iterations").toInt());
    }
  
    bool run(Mesh* mesh, bool autoSegment, bool selectBadVertices, int maxRun);
    
  };

  /**
   * \class FixMeshCorners ProcessCellMesh.hpp <MeshProcessCellMesh.hpp>
   *
   * Fix border triangles, that is triangles with -1 on all 3 vertices. The fixing
   * is done by merging the vertices.
   */
  class mgxBase_EXPORT FixBorderTriangles : public Process 
  {
  public:
    FixBorderTriangles(const Process& process) : Process(process) 
    {
	  setName("Mesh/Cell Mesh/Fix Corner Triangles");
	  setDesc("Fix labelling of triangles in the cell corners.");	
	  setIcon(QIcon(":/images/FixCorners.png"));
	}
  
    bool run()
    {
      if(!checkState().mesh(MESH_NON_EMPTY))
        return false;
      return run(currentMesh());
    }
  
    bool run(Mesh* mesh);

  };
   
  // Convert to a normal mesh
  class mgxBase_EXPORT ConvertToMgxm : public Process 
	{
  public:
    ConvertToMgxm(const Process &process) : Process(process) 
    {
	  setName("Mesh/Cell Mesh/Convert to a normal mesh");
	  setDesc("Convert any mesh back to a normal (MGXM) mesh");	
	  setIcon(QIcon(":/images/MakeCells.png"));
	}
 
    // Process default parameters
    bool run()
    {
      if(!checkState().mesh(MESH_NON_EMPTY))
        return(false);
      return run(currentMesh());
    }
    // Convert the mesh
    bool run(Mesh* mesh);

  };

  /**
   * \class ConvertToMgxc MeshProcessCellMesh.hpp <MeshProcessCellMesh.hpp>
   * 
	 * Convert a segmented mesh into a cell mesh (MGXC).
	 * The simplified mesh contains only vertices for cell centers and cell outlines.
	 * The process will also convert a 2D cell tissue (MGX2D) back to a cell mesh (MGXC)
   */
  class mgxBase_EXPORT ConvertToMgxc : public Process 
	{
  public:
    ConvertToMgxc(const Process &process) : Process(process) 
    {
	  setName("Mesh/Cell Mesh/Convert to a cell mesh");
	  setDesc("Convert a segmented mesh into a cell mesh (MGXC). \n"
	  "The simplified mesh contains only vertices for cell centers and cell outlines."
	  "The process will also convert a 2D cell tissue (MGX2D) back to a cell mesh (MGXC).");
	  setIcon(QIcon(":/images/MakeCells.png"));
	  
	  addParm("Max Wall Length (µm)","Length between vertices to keep, 0 keeps only junctions, -1 keeps everything.","-1");	// 0	
	}
 
    bool run()
    {
      if(!checkState().mesh(MESH_NON_EMPTY))
        return false;
      return run(currentMesh(), parm("Max Wall Length (µm)").toDouble());
    }
  
    bool run(Mesh* mesh, double wallMax);

  };

  // Convert to 2D cell tissue
  class mgxBase_EXPORT ConvertToMgx2d : public Process 
	{
  public:
    ConvertToMgx2d(const Process &process) : Process(process) 
    {
	  setName("Mesh/Cell Mesh/Convert to 2D Cell Tissue");
	  setDesc("Convert a cell (MGXC) mesh to a 2D Cellular Tissue (MGX2D)");
	  setIcon(QIcon(":/images/MakeCells.png"));
	  
	  addParm("Max Wall Length (µm)","Length between vertices to keep, 0 keeps only junctions, -1 keeps everything.","-1");	// 0	
	}
 
    // Process default parameters
    bool run()
    {
      if(!checkState().mesh(MESH_NON_EMPTY))
        return(false);
      return run(currentMesh(), parm("Max Wall Length (µm)").toDouble());
    }
    // Convert the mesh
    bool run(Mesh* mesh, double wallMax);

  };

  // Convert to 3D cell tissue
  class mgxBase_EXPORT ConvertToMgx3d : public Process 
	{
  public:
    ConvertToMgx3d(const Process &process) : Process(process) 
    {
	  setName("Mesh/Cell Mesh/Convert to 3D Cell Tissue");
	  setDesc("Convert a (MGXM) mesh that is a group of closed 3D cells to a 3D Cellular Tissue (MGX3D)");
	  setIcon(QIcon(":/images/MakeCells.png"));
	  
	  addParm("Tolerance (µm)","Tolerance for merging vertices. Vertices closer than this will be merged.",".01");
	  addParm("Neighbour Min Area","Cells with at least this shared area will be considered as neighbors",".01");
	}
 
    // Process default parameters
    bool run()
    {
      if(!checkState().mesh(MESH_NON_EMPTY))
        return(false);
      return run(currentMesh(), parm("Tolerance (µm)").toDouble(), parm("Neighbour Min Area").toDouble());
    }
    // Convert the mesh
    bool run(Mesh* mesh, double tolerance, double neighborMinArea);

  };

	 /**
   * \class SurfaceFromMGX3D ProcessSelection.hpp <MeshProcessCellMesh.hpp>
   *
   * Extracts a normal MGXM mesh from a 3D tissue mesh (MGX3D), keeping only 
	 * vertices which do not belong to shared faces. New mesh is stored in inactive
	 * stack. Vertices label in the new mesh correspond to cell labels in the original 
	 * 3D mesh. 
   */
  class mgxBase_EXPORT SurfaceFromMGX3D : public Process
  {
  public:
    SurfaceFromMGX3D(const Process& process) : Process(process) 
    {
	  setName("Mesh/Cell Mesh/Tools/Surface mesh from MGX3D");
	  setDesc("Convert MGX3D to normal mesh, keeping only triangles not shared between cells.");	
	  setIcon(QIcon(":/images/MakeCells.png"));
	}
  
    bool run()
    {
      if(!checkState().mesh(MESH_NON_EMPTY))
        return false;
      Mesh* mesh1, *mesh2;
      if(currentMesh() == mesh(0)) {
        mesh1 = mesh(0);
        mesh2 = mesh(1);
      } else if(currentMesh() == mesh(1)) {
        mesh2 = mesh(0);
        mesh1 = mesh(1);
			} else
      return false;	
      
			return run(mesh1, mesh2);
    }
  
    bool run(Mesh* mesh1, Mesh* mesh2);

  };


 /**
   * \class UnlabelPieces MeshProcessCellMesh.hpp <MeshProcessCellMesh.hpp>
   *
   * Find multiple separate pieces of the same label, keep only the biggest piece and unlabel (label -1) the rest
   */
  class mgxBase_EXPORT RelabelFragments : public Process
  {
  public:
    RelabelFragments(const Process& process)
      : Process(process)
    {
	  setName("Mesh/Cell Mesh/Tools/Relabel Fragments");
	  setDesc("Find multiple separate pieces of the same label, keep largest piece and relabel or unlabel (label 0) the rest \n"
	  "Unlabel for Threshold -1, Relabel for > -1");
	  setIcon(QIcon(":/images/CHull.png"));
	  
	  addParm("Relabel Threshold","Relabel Threshold in Vtxs. Set to -1 if pieces should be unlabeled.","-1");	// 0					
	}
	
    bool run(){
        Mesh *m = currentMesh();
        return run(m, parm("Relabel Threshold").toInt());
      }
   bool run(Mesh *mesh1, int relabelThreshold);

  };


 /**
   * \class ExtendCells MeshProcessCellMesh.hpp <MeshProcessCellMesh.hpp>
   *
   * ExtendCells
   */
  class mgxBase_EXPORT ExtendCells : public Process
  {
    public:
      ExtendCells(const Process& process) : Process(process) 
      { 
		setName("Mesh/Cell Mesh/Tools/Extend Cells");
		setDesc("Extends cells into unlabeled regions");  
		setIcon(QIcon(":/images/CHull.png"));
	  }

      bool run(){
        Mesh *m = currentMesh();
        return run(m);
      }
      bool run(Mesh *m);

  };

}
#endif
