//
// 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 IMPLICIT_DEFORMATION_PROCESSES_HPP
#define IMPLICIT_DEFORMATION_PROCESSES_HPP

#include "ImplicitDeformationConfig.hpp"

#include <Process.hpp>
#include <Progress.hpp>
#include <Geometry.hpp>

#include <Mesh.hpp>

#include "ImplicitDeformation.hpp"
#include <MeshProcessPDG.hpp> // JunctionInfo, CheckCorrespodance
#include <GraphUtils.hpp>


namespace mgx
{

  typedef std::pair<vertex, vertex> VtxVtxP;
  typedef std::pair<int,Point3d> IntPoint3dP;
  typedef std::pair<IntInt,double> IntIntDoubleP;




class ImplicitDeformation_EXPORT DeformMesh : public Process
  {
  public:
    DeformMesh(const Process& process) : Process(process)
    {
	  setName("Mesh/Deformation/Mesh 2D/Deform Mesh");
	  setDesc("Deform the current mesh based on the parent labelling of the other mesh");
	  setIcon(QIcon(":/images/Deformation.png"));
	}

    bool run()
    {
      Mesh* m = currentMesh();
      Mesh* m2 = mesh(0);
      if(m == mesh(0)) m2 = mesh(1);
      return run(m, m2);
    }
    bool run(Mesh* m, Mesh* m2);

  };

class ImplicitDeformation_EXPORT DeformationProjection : public Process
  {
  public:
    DeformationProjection(const Process& process) : Process(process)
    {
	  setName("Mesh/Deformation/Mesh 2D/Mesh Projection");
	  setDesc("Projects the current mesh based on the parent labeling in the other mesh onto the other mesh.");
	  setIcon(QIcon(":/images/Deformation2.png"));

	  addParm("Project Vertices","Project Vertices","All", QStringList() << "All" << "Centers" << "Junctions" << "Centers + Junctions");
	}

    bool run()
    {
      Mesh* m = currentMesh();
      Mesh* m2 = mesh(0);
      if(m == mesh(0)) m2 = mesh(1);
      return run(m, m2, parm("Project Vertices"));
    }

    bool run(Mesh* m, Mesh* m2, QString projVtxs);

  };

class ImplicitDeformation_EXPORT SetCorrectParents : public Process
  {
  public:
    SetCorrectParents(const Process& process) : Process(process)
    {
	  setName("Mesh/Deformation/Set Correct Parents");
	  setDesc("Sets the current parent labelling of the other mesh as correct parents and saves them into a attribute map");
	  setIcon(QIcon(":/images/MakeHeatMap.png"));
	}

    bool run()
    {
      Mesh* m = currentMesh();
      Mesh* m2 = mesh(0);
      if(m == mesh(0)) m2 = mesh(1);
      return run(m, m2);
    }
    bool run(Mesh* m, Mesh* m2);

  };

class ImplicitDeformation_EXPORT RestoreCorrectParents : public Process
  {
  public:
    RestoreCorrectParents(const Process& process) : Process(process)
    {
	  setName("Mesh/Deformation/Restore Correct Parents");
	  setDesc("Restores the correct parent labelling of the other mesh from the attribute map");
	  setIcon(QIcon(":/images/MakeHeatMap.png"));
	}

    bool run()
    {
      Mesh* m = currentMesh();
      Mesh* m2 = mesh(0);
      if(m == mesh(0)) m2 = mesh(1);
      return run(m, m2);
    }

    bool run(Mesh* m, Mesh* m2);

  };

class ImplicitDeformation_EXPORT CompareParents : public Process
  {
  public:
    CompareParents(const Process& process) : Process(process)
    {
	  setName("Mesh/Deformation/Tools/Compare Parents");
	  setDesc("Load the same mesh with different parent labels and it will compare them");
	  setIcon(QIcon(":/images/MakeHeatMap.png"));

	  addParm("Select Errors","Select Errors","Yes",booleanChoice());
	}

    bool run()
    {
      Mesh* m = currentMesh();
      Mesh* m2 = mesh(0);
      if(m == mesh(0)) m2 = mesh(1);
      return run(m, m2, stringToBool(parm("Select Errors")));
    }
    bool run(Mesh* m, Mesh* m2, bool selectErrors);

  };

class ImplicitDeformation_EXPORT SetMorphOrigin : public Process
  {
  public:
    SetMorphOrigin(const Process& process) : Process(process)
    {
    setName("Mesh/Deformation/Morphing/Set Origin");
	  setDesc("Sets the origin of the coordinates");
	  setIcon(QIcon(":/images/MorphAxis1.png"));
    }
    bool run()
    {
      Mesh* m = currentMesh();
      return run(m);
    }
    bool run(Mesh* m);

  };

class ImplicitDeformation_EXPORT SetMorphAxis : public Process
  {
  public:
    SetMorphAxis(const Process& process) : Process(process)
    {
	  setName("Mesh/Deformation/Morphing/Set Axis");
	  setDesc("Sets the axis of the coordinates");
	  setIcon(QIcon(":/images/MorphAxis1.png"));
	}

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

  };

class ImplicitDeformation_EXPORT SetMorphAxis2 : public Process
  {
  public:
    SetMorphAxis2(const Process& process) : Process(process)
    {
	  setName("Mesh/Deformation/Morphing/Set Axis 2");
	  setDesc("Sets the axis of the coordinates");
	  setIcon(QIcon(":/images/MorphAxis2.png"));
	}

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

    bool run(Mesh* m);

  };


class ImplicitDeformation_EXPORT MorphMesh : public Process
  {
  public:
    MorphMesh(const Process& process) : Process(process)
    {
	  setName("Mesh/Deformation/Morphing/Simple Morphing");
	  setDesc("Gradually changes a mesh into the other mesh using an implicit deformation. \n"
	  "Set parameters regarding the deformation the the Create Implicit Deformation 2D process");
	  setIcon(QIcon(":/images/Morph.png"));

	  addParm("Steps","Steps","10");
	  addParm("ms per step","ms per step","100");


		}

    bool run()
    {
      Mesh* m = currentMesh();
      Mesh* m2 = mesh(0);
      if(m == mesh(0)) m2 = mesh(1);
      return run(m, m2, parm("Steps").toInt(), parm("ms per step").toInt());
    }

    bool run(Mesh* m, Mesh* m2, int steps, int msStep);

  };

class ImplicitDeformation_EXPORT MorphMeshCreatePositions : public Process
  {
  public:
    MorphMeshCreatePositions(const Process& process) : Process(process)
    {

	  setName("Mesh/Deformation/Morphing/Create Morphing");
	  setDesc("Gradually changes a mesh into the other mesh using an implicit deformation. \n"
	  "Set parameters regarding the deformation the the Create Implicit Deformation 2D process");
	  setIcon(QIcon(":/images/Morph.png"));

	  addParm("Impl Def Attr Map 1","Impl Def Attr Map 1","ID 1");
	  addParm("Impl Def Attr Map 2","Impl Def Attr Map 2","ID 2");
	  addParm("Impl Def Attr Map 3","Impl Def Attr Map 3","ID 3");
	  addParm("Impl Def Attr Map 4","Impl Def Attr Map 4","ID 4");
	  addParm("Impl Def Attr Map 5","Impl Def Attr Map 5","ID 5");
	  addParm("Impl Def Attr Map 6","Impl Def Attr Map 6","ID 6");
	}

    bool run()
    {
      Mesh* m = currentMesh();
      return run(m, parm("Impl Def Attr Map 1"), parm("Impl Def Attr Map 2"), parm("Impl Def Attr Map 3"), parm("Impl Def Attr Map 4"), parm("Impl Def Attr Map 5"), parm("Impl Def Attr Map 6"));
    }

    bool run(Mesh* m, QString def1, QString def2, QString def3, QString def4, QString def5, QString def6);

  };


class ImplicitDeformation_EXPORT CreateSignalProjection : public Process
  {
  public:
    CreateSignalProjection(const Process& process) : Process(process)
    {
	  setName("Mesh/Deformation/Morphing/Create Signal Projection");
	  setDesc("Creates a signal attribute map for a morphing animation.\n"
    "Requires the to-be-morphed mesh as active mesh and the target mesh as non-active.\n"
    "Also requires the position attribute maps that are created using the process Create Morphing Animation and the correct parent labels on the target mesh.\n"
    "\n"
    "When doing a backward morphing (T2 to T1): Have the parent labels loaded (in T2) and turn Use Parents on\n"
    "When doing a forward morphing (T1 to T2): Have the parent labels loaded (in T2), then copy parents to labels and turn Use Parents off\n");
	  setIcon(QIcon(":/images/Morph.png"));

	  addParm("Apply Def Until","Apply Def Until","1");
    addParm("Use Parents","Use Parents when doing backward projection","Yes",booleanChoice());
	}

    bool run()
    {
      Mesh* m = currentMesh();
      Mesh* m2 = mesh(1);
      if(m == mesh(1)) m2 = mesh(0);
      return run(m, m2, parm("Apply Def Until").toInt(), stringToBool(parm("Use Parents")));
    }

    bool run(Mesh* m, Mesh* m2, int useDef, bool useParentLabels);

  };


class ImplicitDeformation_EXPORT CreateHeatProjection : public Process
  {
  public:
    CreateHeatProjection(const Process& process) : Process(process)
    {
    setName("Mesh/Deformation/Morphing/Create Heat Projection");
    setDesc("Creates a heat attribute map for a morphing animation.\n"
    "Requires the to-be-morphed mesh as active mesh with the desired heat map from the previous time point loaded onto the cell labels of the mesh\n"
    "(It is needed to export the heat map of the previous time point from parent ot cell labels!)");
    setIcon(QIcon(":/images/Morph.png"));

    addParm("Apply Def Until","Apply Def Until","1");
  }

    bool run()
    {
      Mesh* m = currentMesh();
      return run(m, parm("Apply Def Until").toInt());
    }

    bool run(Mesh* m, int useDef);

  };

class ImplicitDeformation_EXPORT LoadSignalProjection : public Process
  {
  public:
    LoadSignalProjection(const Process& process) : Process(process)
    {
    setName("Mesh/Deformation/Morphing/Load Signal Attribute");
    setDesc("Loads a signal attribute map onto the active mesh");
    setIcon(QIcon(":/images/Morph.png"));

    addParm("Attr Map","Attr Map","");
    }

    bool run()
    {
      Mesh* m = currentMesh();
      return run(m, parm("Attr Map"));
    }
    bool run(Mesh* m, QString attrName);
  };



class ImplicitDeformation_EXPORT MorphMeshMulti : public Process
  {
  public:
    MorphMeshMulti(const Process& process) : Process(process)
    {
	  setName("Mesh/Deformation/Morphing/Run Morphing");
	  setDesc("Gradually changes a mesh into the other mesh using an implicit deformation. \n"
	  "Set parameters regarding the deformation the the Create Implicit Deformation 2D process");
	  setIcon(QIcon(":/images/Morph.png"));

	  addParm("Steps","Steps","10");
	  addParm("pause ms per step","pause ms per step","100");
	  addParm("Display Heat Map","Display Heat Map","No", QStringList() << "No" << "Growth" << "Growth Backwards" << "Anisotropy" << "Cell Area" << "Attribute (Standard)" << "Attribute (TimeLapse)");
	  addParm("Use Spline","Use Spline","Yes",booleanChoice());
	  addParm("Heat Min","Heat Min","1.0");
	  addParm("Heat Max","Heat Max","1.05");
	  addParm("Also Morph Other Mesh","Also Morph Other Mesh","No",booleanChoice());
	  addParm("Signal Blending","Signal Blending","No",booleanChoice());
    addParm("Rotation X","Rotation X","0");
    addParm("Rotation Y","Rotation Y","0");
    addParm("Rotation Z","Rotation Z","0");
		}

    bool run()
    {
      Mesh* m = currentMesh();
      Mesh* m2 = otherMesh();
      Stack* s = currentStack();
      Stack* s2 = otherStack();

      return run(s, s2, m, m2, parm("Steps").toInt(), parm("pause ms per step").toInt(), parm("Display Heat Map"), stringToBool(parm("Use Spline")), parm("Heat Min").toDouble(), parm("Heat Max").toDouble(),
        stringToBool(parm("Also Morph Other Mesh")), stringToBool(parm("Signal Blending")), parm("Rotation X").toDouble(), parm("Rotation Y").toDouble(), parm("Rotation Z").toDouble());
    }

    bool run(Stack* s, Stack* s2, Mesh* m, Mesh* m2, int steps, int msStep, QString heatmap,
     bool useSpline, double heatMin, double heatMax, bool morphOther, bool signalBlending, double rotX, double rotY, double rotZ);

  };

class ImplicitDeformation_EXPORT ResetMeshMorphing : public Process
  {
  public:
    ResetMeshMorphing(const Process& process) : Process(process)
    {
	  setName("Mesh/Deformation/Reset Mesh");
	  setDesc("Resets the morphed mesh to its initial state");
	  setIcon(QIcon(":/images/DeformationReset.png"));

	  addParm("Reset Mesh Vertices","Reset Mesh Vertices","Yes",booleanChoice());
	  addParm("Reset Transformation","Reset Transformation","Yes",booleanChoice());
	}

    bool run()
    {
      Mesh* m = currentMesh();
      return run(m, stringToBool(parm("Reset Mesh Vertices")), stringToBool(parm("Reset Transformation")));
    }

    bool run(Mesh* m, bool resetMesh, bool resetTransf);

  };


class ImplicitDeformation_EXPORT CreateImplicitDeformation2D : public Process
  {
  public:
    CreateImplicitDeformation2D(const Process& process) : Process(process)
    {
	  setName("Mesh/Deformation/Mesh 2D/Create Deformation Cell Surface");
	  setDesc("Creates an implicit deformation object based on the parameters and the labels on the current mesh and the parents on the other mesh.");
	  setIcon(QIcon(":/images/DeformationCalc.png"));

	  addParm("Direction","Direction of deformation function. The deformation will be saved in the origin mesh.","active -> other", QStringList() << "active -> other" << "other -> active");
	  addParm("CG Steps","Number of Steps for the Conjugant Gradient method. Set to -1 to direrctly solve instead.","-1");
	  addParm("Projection Input","Projection Input","Centers", QStringList() << "Centers" << "Junctions" << "Both" << "None (only manual)");
    addParm("Use Manual Landmarks","Use Manual Landmarks","No",booleanChoice());
	  addParm("Junction Merging Threshold (um)","Junction Merging Threshold (um)","1.0");
	  addParm("Force Recalculation","Force Recalculation","No",booleanChoice());
	}

    bool run()
    {
      Mesh* m = currentMesh();
      Mesh* m2 = mesh(0);
      if(m == mesh(0)) m2 = mesh(1);

      vvGraph& S = m->graph();
      vvGraph& S2 = m2->graph();

      mgxmToMgxc(S, T1, 0);
      mgxmToMgxc(S2, T2, 0);

      return run(m, m2, T1, T2, parm("Direction"), parm("CG Steps").toInt(), parm("Projection Input"), parm("Junction Merging Threshold (um)").toDouble());
    }
    bool run(Mesh* m, Mesh* m2, vvGraph& T1, vvGraph& T2, QString direction, int cgSteps, QString projInput, double distanceThreshold, std::set<int> ignoreLabels,bool updateNormals=true);

    bool run(Mesh* m, Mesh* m2, vvGraph& T1, vvGraph& T2, QString direction, int cgSteps, QString projInput, double distanceThreshold);

    ImplicitDeformation impDef;
    JunctionInformation jInfo;
    vvGraph T1, T2;
  };


class ImplicitDeformation_EXPORT CreateImplicitDeformation3D : public Process
  {
  public:
    CreateImplicitDeformation3D(const Process& process) : Process(process)
    {
	  setName("Mesh/Deformation/Mesh 3D/Create Deformation Cell Volumes");
	  setDesc("Creates an implicit deformation object based on the parameters and the labels on the current mesh and the parents on the other mesh.");
	  setIcon(QIcon(":/images/DeformationCalc.png"));

	  addParm("Direction","Direction","active -> other", QStringList() << "active -> other" << "other -> active");
	  addParm("CG Steps","Number of Steps for the Conjugant Gradient method. Set to -1 to direrctly solve instead.","-1");
	  addParm("Input Centers","Input Centers","Yes",booleanChoice());
	  addParm("Input Inside Walls","Input Inside Walls","No",booleanChoice());
	  addParm("Input Outside Walls","Input Outside Walls","No",booleanChoice());
	  addParm("Input Junctions","Input Junctions","No",booleanChoice());
    addParm("Use Manual Landmarks","Use Manual Landmarks","No",booleanChoice());
    addParm("Point Distance Threshold","Data points must have at least this distance (otherwise they will be averaged, -1 to ignore)","-1");
	  addParm("Consider Explosion","Consider Explosion","No",booleanChoice());
	  addParm("Force Recalculation","Force Recalculation","Yes",booleanChoice());
	}

    bool run()
    {
      Mesh* m = currentMesh();
      Mesh* m2 = mesh(0);
      if(m == mesh(0)) m2 = mesh(1);

      return run(m, m2, parm("Direction"), parm("CG Steps").toInt(), stringToBool(parm("Input Centers")), stringToBool(parm("Input Inside Walls")), stringToBool(parm("Input Outside Walls")), stringToBool(parm("Input Junctions")),
       stringToBool(parm("Use Manual Landmarks")), stringToBool(parm("Consider Explosion")));
    }
    bool run(Mesh* m, Mesh* m2, QString direction, int cgSteps, bool inputCenters, bool inputWalls, bool inputWallsOut, bool inputJunctions,
      bool useManualLandmarks, bool considerExplosion);

    ImplicitDeformation impDef;
    JunctionInformation jInfo;
    vvGraph T1, T2;
  };

  class ImplicitDeformation_EXPORT DeformationParentLabeling : public Process
  {
  public:
    DeformationParentLabeling(const Process& process) : Process(process)
    {
	  setName("Mesh/Deformation/Mesh 2D/Auto Parent Labeling Step 2D");
	  setDesc("Does one step of the auto parent labelling");
	  setIcon(QIcon(":/images/AutoParent1.png"));

	  addParm("Use Fixed Parents","If Yes: Always consider fixed parents as correct","Yes",booleanChoice());
	  addParm("Find Cells","Find Cells","Junction Projection", QStringList() << "Nearest Center" << "Nearest Center Threshold" << "Junction Projection");
	  addParm("Omit Cells %","Omit Cells %","50");
	  addParm("Correct Errors","Correct Errors","Yes",booleanChoice());
	}

    bool run()
    {
      Mesh* m = currentMesh();
      Mesh* m2 = mesh(0);
      if(m == mesh(0)) m2 = mesh(1);
      vvGraph& S = m->graph();
      vvGraph& S2 = m2->graph();

      vvGraph T1, T2;
      mgxmToMgxc(S, T1, 0);
      mgxmToMgxc(S2, T2, 0);


      return run(m, m2, T1, T2, stringToBool(parm("Use Fixed Parents")), parm("Find Cells"), parm("Omit Cells %").toDouble(), stringToBool(parm("Correct Errors")));
    }

    bool run(Mesh* m, Mesh* m2, vvGraph& T1, vvGraph& T2, bool useFixedParents, QString findCells, double omitCells, bool correctErrors,std::map<IntInt, double> * _neighMapS1=NULL);

  };

class ImplicitDeformation_EXPORT AutoFillParents : public Process
  {
  public:
    AutoFillParents(const Process& process) : Process(process)
    {
	  setName("Mesh/Deformation/Mesh 2D/Auto Parent Labelling 2D");
	  setDesc("Repeatedly runs auto parent labelling steps");
	  setIcon(QIcon(":/images/AutoParent2.png"));

	  addParm("Max Steps","Max Steps","10");
	  addParm("Auto Fix Parents Depth","Automatically fix the parents when they are at least this far away from unlabelled regions","-1");
	  addParm("Retreat %","Retreat %","0");
	}

    bool run()
    {
      Mesh* m = currentMesh();
      Mesh* m2 = mesh(0);
      if(m == mesh(0)) m2 = mesh(1);
      return run(m, m2, parm("Max Steps").toInt(), parm("Auto Fix Parents Depth").toInt(), parm("Retreat %").toDouble());
    }

    bool run(Mesh* m, Mesh* m2, int steps, int fixDistance, double retreat);

  };


  class ImplicitDeformation_EXPORT DeformationParentLabeling3D : public Process
  {
  public:
    DeformationParentLabeling3D(const Process& process) : Process(process)
    {
	  setName("Mesh/Deformation/Mesh 3D/Auto Parent Labeling Step 3D");
	  setDesc("Runs one step of the automatic parent labelling for volumetric cells");
	  setIcon(QIcon(":/images/AutoParent1.png"));

	  addParm("CG Step","Number of Steps for the Conjugant Gradient method. Set to -1 to direrctly solve instead.","-1");
	  addParm("Correct Errors","Correct Errors","No",booleanChoice());
	  addParm("Consider Explosion","Consider Explosion","Yes",booleanChoice());
		}

    bool run()
    {
      Mesh* m = currentMesh();
      Mesh* m2 = mesh(0);
      if(m == mesh(0)) m2 = mesh(1);
      return run(m, m2, parm("CG Step").toInt(), stringToBool(parm("Correct Errors")), stringToBool(parm("Consider Explosion")));
    }

    bool run(Mesh* m, Mesh* m2, int cgStep, bool autoCorrect, bool considerExplosion);

  };

class ImplicitDeformation_EXPORT DeformMesh3D : public Process
  {
  public:
    DeformMesh3D(const Process& process) : Process(process)
    {
	  setName("Mesh/Deformation/Mesh 3D/Deform Mesh");
	  setDesc("Deform the current mesh based on the parent labelling of the other mesh");
	  setIcon(QIcon(":/images/DeformationRed.png"));

	  addParm("Force Recalculation","Force Recalculation","No", booleanChoice());
	}

    bool run()
    {
      Mesh* m = currentMesh();
      Mesh* m2 = mesh(0);
      if(m == mesh(0)) m2 = mesh(1);
      return run(m, m2, stringToBool(parm("Force Recalculation")));
    }

    bool run(Mesh* m, Mesh* m2, bool forceRecalc);

  };

class ImplicitDeformation_EXPORT CheckCorrespondence3D : public Process
  {
  public:
    CheckCorrespondence3D(const Process& process) : Process(process)
    {
	  setName("Mesh/Deformation/Mesh 3D/Check Correspondance 3D");
	  setDesc("Compares the neighborhoods of cells in the current mesh and the the parents of the other mesh \n"
	  "Creates a heat map with the numbers of wrong neighbors");
	  setIcon(QIcon(":/images/MakeHeatMap.png"));

	  addParm("Min Shared Wall","Min Shared Wall","0.01");
	  addParm("Type","Type","Walls", QStringList() << "Walls" << "Neighbors");
	  addParm("Existing Connection Factor","Existing Connection Factor","1.0");
	  addParm("Error Threshold","Error Threshold","1.0");
	}

    bool run()
    {
      Mesh* m = currentMesh();
      Mesh* m2 = mesh(0);
      if(m == mesh(0)) m2 = mesh(1);

      return run(m, m2, parm("Min Shared Wall").toDouble(), parm("Type"), parm("Existing Connection Factor").toDouble(), parm("Error Threshold").toDouble());
    }

    bool run(Mesh* m, Mesh* m2, double minSharedWall, QString type, double factor, double threshold);

  };

  class ImplicitDeformation_EXPORT DistanceWaterfront : public Process
  {
  public:
    DistanceWaterfront(const Process& process) : Process(process)
    {
	  setName("Mesh/Deformation/Mesh 2D/Distance Waterfront");
	  setDesc("Creates a heat map based on how far a parent labelled cell is away from an unlabelled region");
	  setIcon(QIcon(":/images/MakeHeatMap.png"));
	}

    bool run()
    {
      Mesh* m = currentMesh();
      Mesh* m2 = mesh(0);
      if(m == mesh(0)) m2 = mesh(1);
      IntFloatAttr heatMap;

      std::map<IntInt, double> neighMapS1;
      neighborhood2D(m->graph(), neighMapS1);

      return run(m, m2, neighMapS1, heatMap);
    }

    bool run(Mesh* m, Mesh* m2, std::map<IntInt, double>& neighMapS1, IntFloatAttr& heatMap, bool updateNormals=false);

  };


class ImplicitDeformation_EXPORT FindIsolatedParents3D : public Process
  {
  public:
    FindIsolatedParents3D(const Process& process) : Process(process)
    {
	  setName("Mesh/Deformation/Mesh 3D/Find Isolated Parent Labels");
	  setDesc("TBD");
	  setIcon(QIcon(":/images/MakeHeatMap.png"));
	}

    bool run()
    {
      Mesh* m = currentMesh();
      Mesh* m2 = mesh(0);
      if(m == mesh(0)) m2 = mesh(1);

      std::set<int> isolatedParents;

      double tolerance = 0.01;

      NhbdGraphInfo info2;
      neighborhoodGraph(m2->graph(), tolerance, info2);

      return run(m2, info2, isolatedParents, true);
    }

    bool run(Mesh* m2, NhbdGraphInfo& info2, std::set<int>& isolatedParents, bool selectP);

  };

  class ImplicitDeformation_EXPORT ExportDeformation : public Process
  {
  public:
    ExportDeformation(const Process& process) : Process(process)
    {
	  setName("Mesh/Deformation/Deformation Save or Export");
	  setDesc("Saves the current deformation in the Attribute Map Implicit Deformation to a CSV file or exports it to a custom Attribute Map.");
	  setIcon(QIcon(":/images/DeformationSave.png"));

	  addParm("Export To","Export To","Attribute Map", QStringList() << "Attribute Map" << "CSV File");
	  addParm("Name","Name","Implicit Deformation Exported");
	}

    bool initialize(QWidget *parent);

    bool run()
    {
      Mesh* m = currentMesh();
      Mesh* m2 = mesh(0);
      if(m == mesh(0)) m2 = mesh(1);

      return run(m, m2, parm("Export To"), parm("Name"));
    }

    bool run(Mesh* m, Mesh* m2, QString exportTo, QString filename);

  };

  class ImplicitDeformation_EXPORT ImportDeformation : public Process
  {
  public:
    ImportDeformation(const Process& process) : Process(process)
    {
	  setName("Mesh/Deformation/Deformation Load From File");
	  setDesc("Imports an existing deformation from a CSV file or an Attribute Map into the default Attribute Map Implicit Deformation");
	  setIcon(QIcon(":/images/DeformationLoad.png"));

	  addParm("Import From","Import From","CSV File", QStringList() << "Attribute Map" << "CSV File");
	  addParm("Name","Name","Implicit Deformation");
	}

    bool initialize(QWidget *parent);

    bool run()
    {
      Mesh* m = currentMesh();
      Mesh* m2 = mesh(0);
      if(m == mesh(0)) m2 = mesh(1);

      return run(m, m2, parm("Import From"), parm("Name"));
    }
    bool run(Mesh* m, Mesh* m2, QString importFrom, QString filename);

  };

class ImplicitDeformation_EXPORT ComputeDeformationGradient : public Process
  {
  public:
    ComputeDeformationGradient(const Process& process) : Process(process)
    {
	  setName("Mesh/Cell Axis/Deformation Gradient/Compute");
	  setDesc("Computes the gradient (derivative) of the currently store deformation function for 2D cells.");
	  setIcon(QIcon(":/images/DeformationGradient.png"));

	  addParm("Mode","Mode","Cell", QStringList() << "Cell" << "Vertex");
	  addParm("Mode Cell","Mode Cell","Average", QStringList() << "Average" << "Center");
	  addParm("Mode Vertex","Mode Vertex","All", QStringList() << "All");
	  addParm("Project onto Vertex or Cell Plane","Project onto Cell Plane","Yes",booleanChoice());
	  addParm("Other Mesh","Other Mesh","Yes",booleanChoice());
	  addParm("Valid Parents Only","Valid Parents Only","Yes",booleanChoice());
	}

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

      Mesh* m2 = mesh(0);
      if(m == mesh(0)) m2 = mesh(1);

      return run(m, m2, parm("Mode"), parm("Mode Cell"), parm("Mode Vertex"), stringToBool(parm("Project onto Vertex or Cell Plane")), stringToBool(parm("Other Mesh")), stringToBool(parm("Valid Parents Only")));
    }

    bool run(Mesh* m, Mesh* m2, QString mode, QString modeCell, QString modeVtx, bool projectOnCell, bool otherMesh, bool validP);

  };

  class ImplicitDeformation_EXPORT ComputeDeformationGradient3D : public Process
  {
  public:
    ComputeDeformationGradient3D(const Process& process) : Process(process)
    {
	  setName("Mesh/Cell Axis 3D/Deformation Gradient/Compute");
	  setDesc("Computes the gradient (derivative) of the currently store deformation function.");
	  setIcon(QIcon(":/images/DeformationGradient.png"));

	  addParm("Mode","Mode","Cell", QStringList() << "Cell" << "Stack" << "Cell Wall" << "Vertex");
	  addParm("Project onto Vertex","Project onto Vertex or Cell Wall Plane (not possible for whole 3D Cells","Yes",booleanChoice());
	  addParm("Other Mesh","Other Mesh","Yes",booleanChoice());
	  addParm("Valid Parents Only","Valid Parents Only","Yes",booleanChoice());
	}

    bool run()
    {
      Mesh* m = currentMesh();
      Stack* s = currentStack();
      Store* s1 = s->currentStore();

      Mesh* m2 = mesh(0);
      if(m == mesh(0)) m2 = mesh(1);

      return run(m, m2, s, s1, parm("Mode"), stringToBool(parm("Project onto Vertex")), stringToBool(parm("Other Mesh")), stringToBool(parm("Valid Parents Only")));
    }

    bool run(Mesh* m, Mesh* m2, Stack* s, Store* s1, QString mode, bool projectOnCell, bool otherMesh, bool validP);
  };


  class ImplicitDeformation_EXPORT DisplayDeformationGradient : public DisplayPDGs
  {
  public:
    DisplayDeformationGradient(const Process& process) : DisplayPDGs(process)
    {
	  setName("Mesh/Cell Axis/Deformation Gradient/Display Cell Gradient");
	  setDesc("Display the principle growth directions");
	}

  };

  class ImplicitDeformation_EXPORT DisplayDeformationGradientCellWall : public DisplayPDGs
  {
  public:
    DisplayDeformationGradientCellWall(const Process& process) : DisplayPDGs(process)
    {
	  setName("Mesh/Cell Axis 3D/Deformation Gradient/Display Cell Wall Gradient");
	  setDesc("Display the principle growth directions");
	}

  };

  class ImplicitDeformation_EXPORT SaveCellAxis3D : public Process
  {
  public:
    SaveCellAxis3D(const Process& process) : Process(process)
    {
	  setName("Mesh/Cell Axis 3D/Cell Axis Save");
	  setDesc("Save 3D Cell Axis to a csv file.");
	  setIcon(QIcon(":/images/CellAxisSave.png"));

	  addParm("Output File","Path to output file","");
	  addParm("Data","Data to be saved","Original Tensor", QStringList() << "Original Tensor" << "Custom Tensor" << "Visualized Axis");
	}

    bool initialize(QWidget* parent);

    bool run()
    {
      if(!checkState().mesh(MESH_NON_EMPTY))
        return false;
      return run(currentMesh(), parm("Output File"), parm("Data"));
    }

    bool run(Mesh* mesh, const QString& filename, const QString& type);

  };


  class ImplicitDeformation_EXPORT DisplayDeformationGradient3D : public Process
  {
  public:
    DisplayDeformationGradient3D(const Process& process) : Process(process)
    {
	  setName("Mesh/Cell Axis 3D/Deformation Gradient/Display Cell Gradient");
	  setDesc("Display the principle growth directions for 3D cells");
	  setIcon(QIcon(":/images/DeformationGradient.png"));

	  addParm("Heatmap","Heatmap","Stretch Max", QStringList()  << "None" << "Stretch Max" << "Stretch Mid" << "Stretch Min" << "Ratio MaxMid" << "Ratio MaxMin" << "Ratio MidMin" << "Product" << "Anisotropy" << "Custom X" << "Custom Y" << "Custom Z");
	  addParm("ScaleHeat","ScaleHeat","Auto", QStringList() << "None" << "Auto" << "Manual");
	  addParm("Heat min","Heat min","1");
	  addParm("Heat max","Heat max","3");
	  addParm("Show Axis","Show Axis","All", QStringList() << "All" << "Max" << "Mid" << "Min" << "Custom All" << "Custom X" << "Custom Y" << "Custom Z" << "None");
	  addParm("Color +","Color +","white");
	  addParm("Color -","Color -","red");
	  addParm("Line Width","Line Width","2.0");
	  addParm("Line Scale","Line Scale","10.0");
	  addParm("Line Offset","Line Offset","0.0");
	  addParm("Threshold","Threshold","0.0");
	  addParm("Project On Surface","Project On Surface","Yes",booleanChoice());
	}

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

      return run(m);
    }
    bool run(Mesh* m)
    {
      return run(m, parm("Heatmap"), parm("Show Axis"), parm("Color +"), parm("Color -"), parm("Line Width").toDouble(), parm("Line Scale").toDouble(), parm("Line Offset").toDouble(), stringToBool(parm("Project On Surface")));
    }

    bool run(Mesh* m, QString heatmapOption, QString axisOption, QColor colorPos, QColor colorNeg, double axisWidth, double axisScale, double axisOffset, bool projectOnSurface);

  };

  class ImplicitDeformation_EXPORT DisplayDeformationGradientVertex3D : public Process
  {
  public:
    DisplayDeformationGradientVertex3D(const Process& process) : Process(process)
    {
	  setName("Mesh/Cell Axis 3D/Deformation Gradient/Display Vertex Gradient");
	  setDesc("Display the principle growth directions on a vertex level");
	  setIcon(QIcon(":/images/DeformationGradient.png"));

	  addParm("Heatmap","Heatmap","Stretch Max", QStringList() << "Stretch Max" << "Stretch Mid" << "Stretch Min" << "Stretch Product" << "Aniso Max/Mid" << "Aniso Max/Min" << "Aniso Mid/Min" << "Stretch Custom");
	  addParm("ScaleHeat","ScaleHeat","Auto");
	  addParm("Heat min","Heat min","1");
	  addParm("Heat max","Heat max","3");
	  addParm("Show Axis","Show Axis","All", QStringList() << "All" << "Cell Plane" << "Max" << "Mid" << "Min" << "Custom");	// 4
	  addParm("Color +","Color +","white");
	  addParm("Color -","Color -","red");
	  addParm("Line Width","Line Width","2.0");
	  addParm("Line Scale","Line Scale","1.0");
	  addParm("Line Offset","Line Offset","0.1");
	  addParm("Threshold","Threshold","0.0");
	  addParm("Min Distance","Min Distance","1.0");
	  addParm("Project On Surface","Project On Surface","Yes",booleanChoice());
	}

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

      return run(m);
    }
    bool run(Mesh* m)
    {
      return run(m, parm("Heatmap"), parm("Show Axis"), parm("Color +"), parm("Color -"), parm("Line Width").toDouble(), parm("Line Scale").toDouble(), parm("Line Offset").toDouble(), parm("Min Distance").toDouble(), stringToBool(parm("Project On Surface")));
    }

    bool run(Mesh* m, QString heatmapOption, QString axisOption, QColor colorPos, QColor colorNeg, double axisWidth, double axisScale, double axisOffset,
      double minDis, bool projectOnSurface);

  };

  class ImplicitDeformation_EXPORT DisplayDeformationGradientOtherMesh : public Process
  {
  public:
    DisplayDeformationGradientOtherMesh(const Process& process) : Process(process)
    {
	  setName("Mesh/Cell Axis 3D/Deformation Gradient/Experimental/Display on Other Mesh");
	  setDesc("TBD");
	  setIcon(QIcon(":/images/DeformationGradient.png"));

	  addParm("Heatmap","Heatmap","Stretch Max", QStringList() << "Stretch Max" << "Stretch Mid" << "Stretch Min" << "Stretch Product 2D" << "Stretch Product 3D" << "Aniso Max/Mid" << "Aniso Max/Min" << "Aniso Mid/Min" << "Stretch Custom");
	  addParm("ScaleHeat","ScaleHeat","Auto");
	  addParm("Heat min","Heat min","1");
	  addParm("Heat max","Heat max","3");
	  addParm("Show Axis","Show Axis","All", QStringList() << "All" << "Cell Plane" << "Max" << "Mid" << "Min" << "Custom");
	  addParm("Color +","Color +","white");
	  addParm("Color -","Color -","red");
	  addParm("Line Width","Line Width","2.0");
	  addParm("Line Scale","Line Scale","1.0");
	  addParm("Line Offset","Line Offset","0.1");
	  addParm("Threshold","Threshold","0.0");
	  addParm("Min Distance","Min Distance","-1.0");
	  addParm("Project On Surface","Project On Surface","Yes",booleanChoice());
	}

    bool run()
    {
      Mesh* m = currentMesh();
      Mesh* m2 = mesh(0);
      if(m == mesh(0)) m2 = mesh(1);

      return run(m);
    }
    bool run(Mesh* m)
    {
      return run(m, parm("Heatmap"), parm("Show Axis"), parm("Color +"), parm("Color -"), parm("Line Width").toDouble(), parm("Line Scale").toDouble(), parm("Line Offset").toDouble(), parm("Min Distance").toDouble(), stringToBool(parm("Project On Surface")));
    }

    bool run(Mesh* m, QString heatmapOption, QString axisOption, QColor colorPos, QColor colorNeg, double axisWidth, double axisScale, double axisOffset,
      double minDis, bool projectOnSurface);

  };


  class ImplicitDeformation_EXPORT Junctions3D : public Process
  {
  public:
    Junctions3D(const Process& process) : Process(process)
    {
	  setName("Mesh/Deformation/Experimental/Find Junctions 3D");
	  setDesc("TBD");
	  setIcon(QIcon(":/images/DeformationGradient.png"));

	  addParm("Junction Mode","Junction Mode","Yes",booleanChoice());
	}

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

      Mesh* m2 = mesh(0);
      if(m == mesh(0)) m2 = mesh(1);

      return run(m, m2, stringToBool(parm("Junction Mode")));
    }

    bool run(Mesh* m, Mesh* m2, bool junctionMode);

  };

  class ImplicitDeformation_EXPORT FlattenMesh : public Process
  {
  public:
    FlattenMesh(const Process& process) : Process(process)
    {
	  setName("Mesh/Cell Mesh/Tools/Flatten Mesh");
	  setDesc("Flattens the cells by keeping the cell borders constant and smoothing the cell interior.");
	  setIcon(QIcon(":/images/DeformationGradient.png"));
	}

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

      return run(m);
    }

    bool run(Mesh* m);

  };

  class ImplicitDeformation_EXPORT LoadPointsCSV : public Process
  {
  public:
    LoadPointsCSV(const Process& process) : Process(process)
    {
    setName("Mesh/Deformation/Landmarks Load From CSV");
    setDesc("Load Landmarks from a CSV file and append them to the Manual Landmarks attribute maps in both meshes.");
    setIcon(QIcon(":/images/DeformationLoad.png"));

    addParm("File Name","File Name","");
  }

    bool initialize(QWidget *parent);

    bool run()
    {
      Mesh* m = currentMesh();
      Mesh* m2 = mesh(0);
      if(m == mesh(0)) m2 = mesh(1);

      return run(m, m2, parm("File Name"));
    }

    bool run(Mesh* m, Mesh* m2, QString filename);

  };

  class ImplicitDeformation_EXPORT SavePointsCSV : public Process
  {
  public:
    SavePointsCSV(const Process& process) : Process(process)
    {
    setName("Mesh/Deformation/Landmarks Save To CSV");
    setDesc("Save Landmarks to a CSV file.");
    setIcon(QIcon(":/images/DeformationSave.png"));

    addParm("File Name","File Name","");
    addParm("Save Centers (2.5D)","Save Centers (2.5D)","No",booleanChoice());
    addParm("Save Junctions (2.5D)","Save Junctions (2.5D)","No",booleanChoice());
    addParm("Save Centroids (3D)","Save Centroids (3D)","No",booleanChoice());
    addParm("Save Manual Landmarks","Save Manual Landmarks","Yes",booleanChoice());
  }

    bool initialize(QWidget *parent);

    bool run()
    {
      Mesh* m = currentMesh();
      Mesh* m2 = mesh(0);
      if(m == mesh(0)) m2 = mesh(1);

      return run(m, m2, parm("File Name"), stringToBool(parm("Save Centers (2.5D)")),
       stringToBool(parm("Save Junctions (2.5D)")),stringToBool(parm("Save Centroids (3D)")), stringToBool(parm("Save Manual Landmarks")));
    }

    bool run(Mesh* m, Mesh* m2, QString filename, bool saveCenters, bool saveJunctions, bool saveCentroids, bool saveManual);

  };


  class ImplicitDeformation_EXPORT RootJunctionData : public Process
  {
  public:
    // Constructor
    RootJunctionData(const Process &process) : Process(process)
    {
	  setName("Mesh/Deformation/Mesh 2D/Root junction info");
	  setDesc("For the root project: print out corresponding junction positoin for two parent-labeled meshes");
	  setIcon(QIcon(":/images/DeformationLoad.png"));
	  addParm("Output File","Path to output file","");
	}
	bool initialize(QWidget* parent);
    bool run()
    {
      if(!checkState().mesh(MESH_NON_EMPTY))
        return false;
      return run(parm("Output File"));
    }
	bool run(const QString& filename);
  };

  class ImplicitDeformation_EXPORT AddManualLandmarks : public Process
  {
  public:
    // Constructor
    AddManualLandmarks(const Process &process) : Process(process)
    {
    setName("Mesh/Deformation/Add Manual Landmarks");
    setDesc("Select vertices in both meshes and use their mean location as additional landmarks for the deformation function");
    setIcon(QIcon(":/images/DeformationLoad.png"));
  }
    bool run()
    {
      Mesh* m1 = currentMesh();
      Mesh* m2 = otherMesh();
      if(!checkState().mesh(MESH_NON_EMPTY))
        return false;
      return run(m1,m2);
    }
  bool run(Mesh* m1, Mesh* m2);
  };

  class ImplicitDeformation_EXPORT ClearManualLandmarks : public Process
  {
  public:
    // Constructor
    ClearManualLandmarks(const Process &process) : Process(process)
    {
    setName("Mesh/Deformation/Clear Manual Landmarks");
    setDesc("Clear all manually assigned landmarks");
    setIcon(QIcon(":/images/DeformationLoad.png"));
  }
    bool run()
    {
      Mesh* m1 = currentMesh();
      Mesh* m2 = otherMesh();
      if(!checkState().mesh(MESH_NON_EMPTY))
        return false;
      return run(m1,m2);
    }
  bool run(Mesh* m1, Mesh* m2);
  };

  class ImplicitDeformation_EXPORT SurfaceExtensionLandmarks : public Process
  {
  public:
    // Constructor
    SurfaceExtensionLandmarks(const Process &process) : Process(process)
    {
    setName("Mesh/Deformation/Experimental/Surface Extension Landmarks");
    setDesc("TBD");
    setIcon(QIcon(":/images/DeformationLoad.png"));
    addParm("Cone Angle","Cone Angle","0.8");
  }
    bool run()
    {
      Mesh* m1 = currentMesh();
      Mesh* m2 = otherMesh();
      Stack *s1 = currentStack();
      Stack *s2 = otherStack();
      if(!checkState().mesh(MESH_NON_EMPTY))
        return false;
      return run(s1,s2,m1,m2);
    }
  bool run(const Stack *s1, const Stack *s2,Mesh* m1, Mesh* m2);
  };


  class ImplicitDeformation_EXPORT DrawManualLandmarks : public Process
  {
  public:
    // Constructor
    DrawManualLandmarks(const Process &process) : Process(process)
    {
    setName("Mesh/Deformation/Experimental/Draw Manual Landmarks");
    setDesc("Visualizes the landmarks saved in the Manual Landmarks attribute maps.\n"
            " Note: This process creates new vertices and selects them. Delete the selected vertices after running the process to restore the original mesh.");
    setIcon(QIcon(":/images/DeformationLoad.png"));
  }
    bool run()
    {
      Mesh* m1 = currentMesh();
      Mesh* m2 = otherMesh();
      Stack *s1 = currentStack();
      Stack *s2 = otherStack();
      if(!checkState().mesh(MESH_NON_EMPTY))
        return false;
      return run(s1,s2,m1,m2);
    }
  bool run(const Stack *s1, const Stack *s2,Mesh* m1, Mesh* m2);
  };




}


#endif
