//
// 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_MEASURES_3D_HPP
#define MESH_PROCESS_MEASURES_3D_HPP

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

namespace mgx
{

  class mgxBase_EXPORT Measure3DVolume : public Process
  {
  public:
    Measure3DVolume(const Process& process) : Process(process) 
    {
	  setName("Mesh/Heat Map/Measures 3D/Geometry/Volume");
	  setDesc("Volume of a 3D cell.");
	  setIcon(QIcon(":/images/MakeHeatMap.png"));		
	
		}
    bool processParms();

    bool run(IntFloatAttr& heatMap);
    bool run()
    {
      if(!checkState().mesh(MESH_ANY))
        return false;
      Mesh *mesh = currentMesh();
      return run(mesh);
    }
  
    bool run(Mesh* mesh){ 
      return run(mesh, mesh->labelHeat());
    }

    bool run(Mesh* mesh, IntFloatAttr& heatMap);

  };


  class mgxBase_EXPORT Measure3DNeighbors : public Process
  {
  public:
    Measure3DNeighbors(const Process& process) : Process(process) 
    {
    setName("Mesh/Heat Map/Measures 3D/Network/Neighbors");
    setDesc("Number of neighbors of a 3D cell.");
    setIcon(QIcon(":/images/MakeHeatMap.png"));   
  
    }
    bool processParms();

    bool run(IntFloatAttr& heatMap);
    bool run()
    {
      if(!checkState().mesh(MESH_ANY))
        return false;
      Mesh *mesh = currentMesh();
      return run(mesh);
    }
  
    bool run(Mesh* mesh){ 
      return run(mesh, mesh->labelHeat());
    }

    bool run(Mesh* mesh, IntFloatAttr& heatMap);

  };


  class mgxBase_EXPORT Measure3DWallArea : public Process
  {
  public:
    Measure3DWallArea(const Process& process) : Process(process) 
    {
	  setName("Mesh/Heat Map/Measures 3D/Geometry/Cell Wall Area");
	  setDesc("Cell Wall Area of a 3D cell");
	  setIcon(QIcon(":/images/MakeHeatMap.png"));	
	}
	
    bool processParms();

    bool run(IntFloatAttr& heatMap);
    bool run()
    {
      if(!checkState().mesh(MESH_ANY))
        return false;
      Mesh *mesh = currentMesh();
      return run(mesh);
    }
  
    bool run(Mesh* mesh){ 
      return run(mesh, mesh->labelHeat());
    }

    bool run(Mesh* mesh, IntFloatAttr& heatMap);

  };
  class mgxBase_EXPORT Measure3DCoordLong : public Process
  {
  public:
    Measure3DCoordLong(const Process& process) : Process(process) 
    {
	  setName("Mesh/Heat Map/Measures 3D/Cell Atlas/Coord Longitudinal");
	  setDesc("Longitudinal Coord of a 3D cell. Requires the Cell Atlas measure maps.");	
	  setIcon(QIcon(":/images/MakeHeatMap.png"));	
	}
	
    bool processParms();

    bool run()
    {
      if(!checkState().mesh(MESH_ANY))
        return false;
      Mesh *mesh = currentMesh();
      return run(mesh);
    }
  
    bool run(Mesh* mesh){ 
      return run(mesh, mesh->labelHeat());
    }

    bool run(Mesh* mesh, IntFloatAttr& heatMap);

  };

  class mgxBase_EXPORT Measure3DCoordCirc : public Process
  {
  public:
    Measure3DCoordCirc(const Process& process) : Process(process) 
    {
	  setName("Mesh/Heat Map/Measures 3D/Cell Atlas/Coord Circumferential");
	  setDesc("Circumferential Coord of a 3D cell. Requires the Cell Atlas measure maps.");
	  setIcon(QIcon(":/images/MakeHeatMap.png"));		
	}
	
    bool processParms();

    bool run(IntFloatAttr& heatMap);
    bool run()
    {
      if(!checkState().mesh(MESH_ANY))
        return false;
      Mesh *mesh = currentMesh();
      return run(mesh);
    }
  
    bool run(Mesh* mesh){ 
      return run(mesh, mesh->labelHeat());
    }

    bool run(Mesh* mesh, IntFloatAttr& heatMap);

  };

  class mgxBase_EXPORT Measure3DCoordRad : public Process
  {
  public:
    Measure3DCoordRad(const Process& process) : Process(process) 
    {
	  setName("Mesh/Heat Map/Measures 3D/Cell Atlas/Coord Radial");
	  setDesc("Radial Coord of a 3D cell. Requires the Cell Atlas measure maps.");	
	  setIcon(QIcon(":/images/MakeHeatMap.png"));
	}
	
    bool processParms();

    bool run(IntFloatAttr& heatMap);
    bool run()
    {
      if(!checkState().mesh(MESH_ANY))
        return false;
      Mesh *mesh = currentMesh();
      return run(mesh);
    }
  
    bool run(Mesh* mesh){ 
      return run(mesh, mesh->labelHeat());
    }

    bool run(Mesh* mesh, IntFloatAttr& heatMap);

  };

    class mgxBase_EXPORT Measure3DSizeLong : public Process
  {
  public:
    Measure3DSizeLong(const Process& process) : Process(process) 
    {
	  setName("Mesh/Heat Map/Measures 3D/Cell Atlas/Cell Length Longitudinal");
	  setDesc("Longitudinal Cell Size of a 3D cell. Requires the Cell Atlas measure maps.");	
	  setIcon(QIcon(":/images/MakeHeatMap.png"));
	  
	}
    bool processParms();

    bool run(IntFloatAttr& heatMap);
    bool run()
    {
      if(!checkState().mesh(MESH_ANY))
        return false;
      Mesh *mesh = currentMesh();
      return run(mesh);
    }
  
    bool run(Mesh* mesh){ 
      return run(mesh, mesh->labelHeat());
    }

    bool run(Mesh* mesh, IntFloatAttr& heatMap);

  };
    class mgxBase_EXPORT Measure3DSizeCirc : public Process
  {
  public:
    Measure3DSizeCirc(const Process& process) : Process(process) 
    {
	  setName("Mesh/Heat Map/Measures 3D/Cell Atlas/Cell Length Circumferential");
	  setDesc("Circumferential Cell Size of a 3D cell. Requires the Cell Atlas measure maps.");	
	  setIcon(QIcon(":/images/MakeHeatMap.png"));
	}
	
    bool processParms();

    bool run(IntFloatAttr& heatMap);
    bool run()
    {
      if(!checkState().mesh(MESH_ANY))
        return false;
      Mesh *mesh = currentMesh();
      return run(mesh);
    }
  
    bool run(Mesh* mesh){ 
      return run(mesh, mesh->labelHeat());
    }

    bool run(Mesh* mesh, IntFloatAttr& heatMap);

  };

    class mgxBase_EXPORT Measure3DSizeRad : public Process
  {
  public:
    Measure3DSizeRad(const Process& process) : Process(process) 
    {
	  setName("Mesh/Heat Map/Measures 3D/Cell Atlas/Cell Length Radial");
	  setDesc("Radial Cell Size of a 3D cell. Requires the Cell Atlas measure maps.");	
	  setIcon(QIcon(":/images/MakeHeatMap.png"));
	}
	
    bool processParms();

    bool run(IntFloatAttr& heatMap);
    bool run()
    {
      if(!checkState().mesh(MESH_ANY))
        return false;
      Mesh *mesh = currentMesh();
      return run(mesh);
    }
  
    bool run(Mesh* mesh){ 
      return run(mesh, mesh->labelHeat());
    }

    bool run(Mesh* mesh, IntFloatAttr& heatMap);

  };

    class mgxBase_EXPORT Measure3DSizeX : public Process
  {
  public:
    Measure3DSizeX(const Process& process) : Process(process) 
    {
	  setName("Mesh/Heat Map/Measures 3D/Geometry/Cell Length X");
	  setDesc("Cell Size in x dimension of a 3D cell");	
	  setIcon(QIcon(":/images/MakeHeatMap.png"));
	}
	
    bool processParms();

    bool run(IntFloatAttr& heatMap);
    bool run()
    {
      if(!checkState().mesh(MESH_ANY))
        return false;
      Mesh *mesh = currentMesh();
      return run(mesh);
    }
  
    bool run(Mesh* mesh){ 
      return run(mesh, mesh->labelHeat());
    }

    bool run(Mesh* mesh, IntFloatAttr& heatMap);

  };

    class mgxBase_EXPORT Measure3DSizeY : public Process
  {
  public:
    Measure3DSizeY(const Process& process) : Process(process) 
    {
	  setName("Mesh/Heat Map/Measures 3D/Geometry/Cell Length Y");
	  setDesc("Cell Size in y dimension of a 3D cell");	
	  setIcon(QIcon(":/images/MakeHeatMap.png"));
	}
    bool processParms();

    bool run(IntFloatAttr& heatMap);
    bool run()
    {
      if(!checkState().mesh(MESH_ANY))
        return false;
      Mesh *mesh = currentMesh();
      return run(mesh);
    }
  
    bool run(Mesh* mesh){ 
      return run(mesh, mesh->labelHeat());
    }

    bool run(Mesh* mesh, IntFloatAttr& heatMap);

  };

  class mgxBase_EXPORT Measure3DSizeZ : public Process
  {
  public:
    Measure3DSizeZ(const Process& process) : Process(process) 
    {
	  setName("Mesh/Heat Map/Measures 3D/Geometry/Cell Length Z");
	  setDesc("Cell Size in z dimension of a 3D cell");	
	  setIcon(QIcon(":/images/MakeHeatMap.png"));
	}
	
    bool processParms();

    bool run(IntFloatAttr& heatMap);
    bool run()
    {
      if(!checkState().mesh(MESH_ANY))
        return false;
      Mesh *mesh = currentMesh();
      return run(mesh);
    }
  
    bool run(Mesh* mesh){ 
      return run(mesh, mesh->labelHeat());
    }

    bool run(Mesh* mesh, IntFloatAttr& heatMap);

  };

  class mgxBase_EXPORT Measure3DSizeCustom : public Process
  {
  public:
    Measure3DSizeCustom(const Process& process) : Process(process) 
    {
	  setName("Mesh/Heat Map/Measures 3D/Geometry/Cell Length Custom");
	  setDesc("Cell Size in custom direction a 3D cell \n"
	  "Requires a valid Custom Directions Attribute Map in the active mesh \n"
	  "(which can be generated using the Custom processes in Cell Axis");
	  setIcon(QIcon(":/images/MakeHeatMap.png"));

	  addParm("Custom Direction","Custom Direction Dimension","X", QStringList() << "X" << "Y" << "Z");

    addParm("Custom Direction Heat","Custom Direction Heat","X", QStringList() << "X" << "Y" << "Z");
    addParm("Attribute Map X","Attribute Map X","customX");
    addParm("Attribute Map Y","Attribute Map Y","customY");
    addParm("Attribute Map Z","Attribute Map Z","customZ");

	}
	
    bool processParms();

    bool run(IntFloatAttr& heatMap);
    bool run()
    {
      if(!checkState().mesh(MESH_ANY))
        return false;
      Mesh *mesh = currentMesh();
      return run(mesh, parm("Custom Direction"), parm("Attribute Map X"), parm("Attribute Map Y"), parm("Attribute Map Z"));
    }
  
    bool run(Mesh* mesh, QString dim, QString attr1, QString attr2, QString attr3){ 
      return run(mesh, dim, attr1, attr2, attr3, mesh->labelHeat());
    }

    bool run(Mesh* mesh, QString dim,  QString attr1, QString attr2, QString attr3, IntFloatAttr& heatMap);

  };


  class mgxBase_EXPORT Measure3DVolumeSurfaceRatio : public Process
  {
  public:
    Measure3DVolumeSurfaceRatio(const Process& process) : Process(process) 
    {
	  setName("Mesh/Heat Map/Measures 3D/Geometry/Volume Surface Ratio");
	  setDesc("Volume Surface Ratio of a 3D cell (root3(volume)/root2(wall area)). Requires the Cell Atlas measure maps.");
	  setIcon(QIcon(":/images/MakeHeatMap.png"));
	  
	  addParm("Root Volume","Root Volume","3.0");
	  addParm("Root Wall Area","Root Wall Area","2.0");	
	
		}
    bool processParms();

    bool run(IntFloatAttr& heatMap);
    bool run()
    {
      if(!checkState().mesh(MESH_ANY))
        return false;
      Mesh *mesh = currentMesh();
      return run(mesh, parm("Root Volume").toDouble(), parm("Root Wall Area").toDouble());
    }
  
    bool run(Mesh* mesh, double powerVol, double powerWall){ 
      return run(mesh, mesh->labelHeat(), powerVol, powerWall);
    }

    bool run(Mesh* mesh, IntFloatAttr& heatMap, double powerVol, double powerWall);

  };

  class mgxBase_EXPORT Measure3DOutsideWallArea : public Process
  {
  public:
    Measure3DOutsideWallArea(const Process& process) : Process(process) 
    {
	  setName("Mesh/Heat Map/Measures 3D/Geometry/Outside Wall Area Ratio");
	  setDesc("Wall Area of a cell not shared with a neighbor cell");	
	  setIcon(QIcon(":/images/MakeHeatMap.png"));
	}
	
    bool processParms();

    bool run(IntFloatAttr& heatMap);
    bool run()
    {
      if(!checkState().mesh(MESH_ANY))
        return false;
      Mesh *mesh = currentMesh();
      return run(mesh);
    }
  
    bool run(Mesh* mesh){ 
      return run(mesh, mesh->labelHeat());
    }

    bool run(Mesh* mesh, IntFloatAttr& heatMap);

  };

  class mgxBase_EXPORT Measure3DOutsideWallAreaPercent : public Process
  {
  public:
    Measure3DOutsideWallAreaPercent(const Process& process) : Process(process) 
    {
	  setName("Mesh/Heat Map/Measures 3D/Geometry/Outside Wall Area Ratio");
	  setDesc("Wall Area of a cell not shared with a neighbor cell");	
	  setIcon(QIcon(":/images/MakeHeatMap.png"));
	}
	
    bool processParms();

    bool run(IntFloatAttr& heatMap);
    bool run()
    {
      if(!checkState().mesh(MESH_ANY))
        return false;
      Mesh *mesh = currentMesh();
      return run(mesh);
    }
  
    bool run(Mesh* mesh){ 
      return run(mesh, mesh->labelHeat());
    }

    bool run(Mesh* mesh, IntFloatAttr& heatMap);

  };

  class mgxBase_EXPORT Measure3DCellDistance : public Process
  {
  public:
    Measure3DCellDistance(const Process& process) : Process(process) 
    {
	  setName("Mesh/Heat Map/Measures 3D/Location/Cell Distance");
	  setDesc("Distance through the tissue to the nearest selected cells.");
	  setIcon(QIcon(":/images/MakeHeatMap.png"));

	  addParm("Wall Weights","Wall Weights","Euclidean", QStringList() << "1 / Wall Length" << "1" << "Euclidean");
    addParm("Restrict connectivity to same cell type", "Restrict connectivity to same cell type", "No", booleanChoice());
	}
	
    bool processParms();

    // bool run(IntFloatAttr& heatMap);
    bool run()
    {
      if(!checkState().mesh(MESH_ANY))
        return false;
      Mesh *mesh = currentMesh();
      return run(mesh, parm("Wall Weights"), stringToBool(parm("Restrict connectivity to same cell type")));
    }
  
    bool run(Mesh* mesh, QString weight, bool cellTypes){ 
      return run(mesh, weight, cellTypes, mesh->labelHeat());
    }

    bool run(Mesh* mesh, QString weight, bool cellTypes, IntFloatAttr& heatMap);

  };


  class mgxBase_EXPORT Measure3DCellDistanceBezierRing : public Process
  {
  public:
    Measure3DCellDistanceBezierRing(const Process& process) : Process(process) 
    {
    setName("Mesh/Heat Map/Measures 3D/Location/Cell Distance Bezier Ring");
    setDesc("Distance through the tissue to the nearest selected cells.");
    setIcon(QIcon(":/images/MakeHeatMap.png"));

    addParm("Wall Weights","Wall Weights","1", QStringList() << "1" << "Euclidean");
    addParm("Restrict connectivity to same cell type", "Restrict connectivity to same cell type", "Yes", booleanChoice());
    addParm("Direct Distance Limit (um)","Direct Distance Limit (um)","20.0");
    addParm("Selected as Direct","Selected as Direct","Yes", booleanChoice());
    addParm("Consider Ring Orientation","Consider Ring Orientation","Yes", booleanChoice());
    addParm("Select Direct Cells","Select Direct Cells","No", booleanChoice());
    addParm("Keep Selected Cells","Keep Selected Cells","Yes", booleanChoice());
  }
  
    bool processParms();

    // bool run(IntFloatAttr& heatMap);
    bool run()
    {
      if(!checkState().mesh(MESH_ANY))
        return false;
      Mesh *mesh = currentMesh();
      return run(mesh, parm("Wall Weights"), stringToBool(parm("Restrict connectivity to same cell type")),
        parm("Direct Distance Limit (um)").toDouble(), stringToBool(parm("Selected as Direct")), 
        stringToBool(parm("Consider Ring Orientation")), stringToBool(parm("Select Direct Cells")), stringToBool(parm("Keep Selected Cells")), mesh->labelHeat());
    }
  
    // bool run(Mesh* mesh, QString weight, bool cellTypes, double thresholdDis, bool selectCells){ 
    //   return run(mesh, weight, cellTypes, thresholdDis, selectCells, mesh->labelHeat());
    // }

    bool run(Mesh* mesh, QString weight, bool cellTypes, double thresholdDis, bool selectedAsDirect, bool considerOrientation, 
      bool selectCells, bool keep, IntFloatAttr& heatMap);

  };

  class mgxBase_EXPORT Measure3DCellCoord : public Process
  {
  public:
    Measure3DCellCoord(const Process& process) : Process(process) 
    {
    setName("Mesh/Heat Map/Measures 3D/Location/Cell Coordinate");
    setDesc("Distance through the tissue to the nearest selected cells.");
    setIcon(QIcon(":/images/MakeHeatMap.png"));

      addParm("Dimension","Dimension","X", QStringList() << "X" << "Y" << "Z" << "Distance Origin"); // 0 
  }

    bool run()
    {
      if(!checkState().mesh(MESH_ANY))
        return false;
      Mesh *mesh = currentMesh();
      Stack* s = currentStack();
      return run(s, mesh, parm("Dimension"),mesh->labelHeat());
    }

    bool run(Stack* s, Mesh* mesh, QString dim, IntFloatAttr& heatMap);

  };

  class mgxBase_EXPORT Measure3DDistanceToMesh : public Process
  {
  public:
    Measure3DDistanceToMesh(const Process& process) : Process(process) 
    {
	  setName("Mesh/Heat Map/Measures 3D/Location/Mesh Distance");
	  setDesc("Distance to the other mesh");	
	  setIcon(QIcon(":/images/MakeHeatMap.png"));
	}

    bool run(IntFloatAttr& heatMap);
    bool run()
    {
      if(!checkState().mesh(MESH_ANY))
        return false;
      Mesh *mesh = currentMesh();
      Mesh *mesh2 = otherMesh();
      return run(mesh, mesh2);
    }
  
    bool run(Mesh* mesh, Mesh* mesh2){ 
      return run(mesh, mesh2, mesh->labelHeat());
    }

    bool run(Mesh* mesh, Mesh* mesh2, IntFloatAttr& heatMap);

  };


  class mgxBase_EXPORT MeasureDistanceToBezier3D : public Process
  {
  public:
    MeasureDistanceToBezier3D(const Process& process) : Process(process) 
    {
	  setName("Mesh/Heat Map/Measures 3D/Location/Distance to Bezier");
	  setDesc("Computes for each cell the distance to a defined Bezier grid or line");
	  setIcon(QIcon(":/images/MakeHeatMap.png"));

	  addParm("Consider Orientation","Consider Orientation","Yes",booleanChoice());	
	  addParm("Reverse Orientation","Reverse Orientation","No",booleanChoice());	
	}
    bool processParms();
    
    bool run()
    {
      if(!checkState().mesh(MESH_ANY))
        return false;
      Mesh *mesh = currentMesh();
      Stack *s1 = currentStack();
      return run(s1, mesh, stringToBool(parm("Consider Orientation")), stringToBool(parm("Reverse Orientation")), mesh->labelHeat());
    }
  
    bool run(const Stack *s1, Mesh* mesh, bool orient, bool reverse, IntFloatAttr& heatMap);

  };


  class mgxBase_EXPORT MeasureBezierLineCoord3D : public Process
  {
  public:
    MeasureBezierLineCoord3D(const Process& process) : Process(process) 
    {
    setName("Mesh/Heat Map/Measures 3D/Location/Bezier Coord");
    setDesc("TBD");
    setIcon(QIcon(":/images/MakeHeatMap.png"));

  }
    bool processParms();
    
    bool run()
    {
      if(!checkState().mesh(MESH_ANY))
        return false;
      Mesh *mesh = currentMesh();
      Stack *s1 = currentStack();
      return run(s1, mesh, mesh->labelHeat());
    }
  
    bool run(const Stack *s1, Mesh* mesh, IntFloatAttr& heatMap);

  };


  class mgxBase_EXPORT MeasureDistanceToBezierPlane3D : public Process
  {
  public:
    MeasureDistanceToBezierPlane3D(const Process& process) : Process(process) 
    {
    setName("Mesh/Heat Map/Measures 3D/Location/Distance to Plane from Bezier");
    setDesc("Computes for each cell the distance to fitted plane of the supporting points of a defined Bezier grid or line");
    setIcon(QIcon(":/images/MakeHeatMap.png"));

    addParm("Consider Orientation","Consider Orientation","Yes",booleanChoice()); 
    addParm("Reverse Orientation","Reverse Orientation","No",booleanChoice());  
  }
    bool processParms();
    
    bool run()
    {
      if(!checkState().mesh(MESH_ANY))
        return false;
      Mesh *mesh = currentMesh();
      Stack *s1 = currentStack();
      return run(s1, mesh, stringToBool(parm("Consider Orientation")), stringToBool(parm("Reverse Orientation")), mesh->labelHeat());
    }
  
    bool run(const Stack *s1, Mesh* mesh, bool orient, bool reverse, IntFloatAttr& heatMap);

  };


}

#endif
