//
// 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 LOBYNESS_PLUGIN_HPP
#define LOBYNESS_PLUGIN_HPP
#include <Process.hpp>
#include <Progress.hpp>
#include <Information.hpp>
#include <MeshProcessCellMesh.hpp>
#include <QFileDialog>

#define DEBUG_LOBYNESS_PLUGIN //if defined outputs debug info and populates mesh2 with geometry visualizing algorithm output


namespace mgx 
{ 

  struct LEC_output{
	int label;
	float radius;
	Point3d centre;
  };


  class LobynessPlugin : public Process
  {
  public:
    LobynessPlugin(const Process& process) : Process(process) 
    {
	  setName("Mesh/Shape Analysis/Lobeyness Measures");
	  setDesc("Lobeyness Plugin \n"
	  "This plugin provides several measures to analyse cell-outlines. If used please cite Sapala et al., eLife 2018 (doi: 10.7554/eLife.32794). \n"
	  "Convex (Perimeter): Ratio of cell-perimeter over that of its convex hull. Takes a value of 1 for convex shapes, and increases with the complexity of the contour. \n"
	  "Convex (Area): Like Convex (Perimeter), but uses areas instead of perimeters. \n"
	  "Circularity (a.k.a. Dissection Index or Roundness): (cell-perimeter)^2 / (4.0*PI*(cell-area)). \n"
	  "Largest Empty Circle (LEC): Distance of the point furthest from the cell contour to the cell wall. \n"
	  "Visbility (Stomata): Estimate of visibility in the cell. Returns 1 for convex shapes, and decreases with the complexity of the contour. \n"
	  "Visibility (Pavement): Simply 1-Visibility(Stomata) \n"
	  "Skeleticity: ratio of LEC of the contour over LEC of the convex hull");
	  setIcon(QIcon(":/images/LobynessPlugin.png"));

	  addParm("Measure","Measure to apply","Convex(Perimeter)", QStringList() << "Convex(Perimeter)" << "Convex(Area)" << "Visibility(Stomata)" << "Visibility(Pavement)" << "Circularity" << "LargestEmptySpace" << "Skeleticity");	// 0
	  addParm("Smoothing its","Iterations of smoothing prior to applying the measure","0");	// 1
	  addParm("Fix mesh","Fix problems with mesh connectivity prior to computing heatmap (runs Fix Border Triangles and Fix Corners)","No",booleanChoice());	// 2
	  addParm("Save LEC","Save LEC position and radius when LEC measure is executed","No",booleanChoice());	// 3
	  addParm("Filename","Filename with LEC info in CSV format","");	// 4					
    }

    //~ CellGraphNew () = default;
    bool run() {
      Mesh *cur_mesh = currentMesh();
      //This should be encapsulated in a complementary sub-routine to 
			//currentMesh(), (e.g. otherMesh())
      Mesh *other_mesh = NULL;
      if(cur_mesh == mesh(0))
        other_mesh = mesh(1);
      else
        other_mesh = mesh(0);
     return run(currentMesh(),other_mesh,parm("Measure"),parm("Smoothing its"),parm("Fix mesh"),parm("Save LEC"),parm("Filename"));
    }

   //Can change lec_save_file using file dialogue
    bool run(Mesh *mesh,Mesh *mesh2, const QString &measure,const QString &epsilon,const QString &fix_mesh_bool,const QString &save_lec,const QString &lec_sav_file);
 
    float Circularity(std::vector<Point3d> contour);
    float ConvexityPerimeter(std::vector<Point3d> contour);
    float ConvexityArea(std::vector<Point3d> contour);
    float VisibilityIndex(std::vector<std::vector<double> > map);
    std::vector<Point3d> ConvexHull(std::vector<Point3d> contour);
    float SignedTriArea(Point3d p1,Point3d p2,Point3d p3);
    float Perimeter(std::vector<Point3d> contour);
    float Area(std::vector<Point3d> contour);
    float IntPoint(Point3d p1,Point3d p2, Point3d q1,Point3d q2);
    void SmoothContour(std::vector<Point3d> &contour,int its,float rate=0.1);
    bool isVisible(int i,int j,const std::vector<Point3d> &contour);
    bool ThroughInterior(int i,int j,const std::vector<Point3d> &contour);
    std::vector<std::vector<double> > ConnectivityMap(std::vector<Point3d> contour);
    float LargestEmptySpace(std::vector<Point3d> contour,int label=-1);
    float Skeleticity(std::vector<Point3d> contour);
 //   float ExteriorDistance(int i,int j,std::vector<Point3d> contour);
    void MakeNeighMap(vvGraph &T, IntFloatAttr &heat_val, std::map<int,std::map<int, double> > &neigh_vals, Mesh *m);
    //This function hasn't been fully tested
    //void NeighAverage(std::map<int, double> &avg_heat_val, std::map<int,std::map<int, double> > &neigh_vals, Mesh *m);
    //This function hasn't been fully tested
    //void NeighStdDev(std::map<int, double> &std_heat_val,std::map<int, double> &avg_heat_val, std::map<int,std::map<int, double> > &neigh_vals, Mesh *m);
 
    //member var
    Mesh *debug_mesh;
    Matrix3d debug_rot;
    double debug_z;
    bool debug_inv_x;
    std::map<vertex, bool> vtxExists; //used in debugging
    QWidget *parent;
    double epsilon;
    int smooth_its;
    bool fix_mesh;
    bool save_LEC;
    QString LEC_CSV;
    std::vector<LEC_output> lec_data;
  };

  class LobynessMeasureConvPeri : public Process
  {
  public:
    LobynessMeasureConvPeri(const Process& process) : Process(process) 
    {
	  setName("Mesh/Heat Map/Measures/Lobeyness/Lobeyness");
	  setDesc("Lobeyness AddOn \n"
	  "Lobeyness Perimeter (inverse of convexity): \n"
    "Ratio of cell-perimeter over that of its convex hull. Takes a value of 1 for convex shapes, and increases with the complexity of the contour. If used please cite Sapala et al., eLife 2018 (doi: 10.7554/eLife.32794).");
	  setIcon(QIcon(":/images/LobynessPlugin.png"));		
    }

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

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

      return run(m,m2);
    }

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

  };


  class LobynessMeasureConvArea : public Process
  {
  public:
    LobynessMeasureConvArea(const Process& process) : Process(process) 
    {
	  setName("Mesh/Heat Map/Measures/Lobeyness/Solidarity");
	  setDesc("Lobeyness AddOn \n"
	  "Solidarity: Ratio of the area of the convex hull over that of the cell. Takes a value of 1 for convex shapes, and increases as cell shape becomes more complicated.  If used please cite Sapala et al., eLife 2018 (doi: 10.7554/eLife.32794).");
	  setIcon(QIcon(":/images/LobynessPlugin.png"));		
    }

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

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

      return run(m,m2);
    }

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

  };


  class LobynessMeasureVisSto : public Process
  {
  public:
    LobynessMeasureVisSto(const Process& process) : Process(process) 
    {
	  setName("Mesh/Heat Map/Measures/Lobeyness/Visibility Stomata");
	  setDesc("Lobeyness AddOn \n"
	  "Visbility (Stomata): Estimate of visibility in the cell. Returns 1 for convex shapes, and decreases with the complexity of the contour.  If used please cite Sapala et al., eLife 2018 (doi: 10.7554/eLife.32794).");	
      setIcon(QIcon(":/images/LobynessPlugin.png"));
    }

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

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

      return run(m,m2);
    }

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

  };


  class LobynessMeasureVisPav : public Process
  {
  public:
    LobynessMeasureVisPav(const Process& process) : Process(process) 
    {
	  setName("Mesh/Heat Map/Measures/Lobeyness/Visibility Pavement");
      setDesc("Lobeyness AddOn \n"
	  "Visibility (Pavement): Simply 1-Visibility(Stomata).  If used please cite Sapala et al., eLife 2018 (doi: 10.7554/eLife.32794).");
	  setIcon(QIcon(":/images/LobynessPlugin.png"));
	
    }

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

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

      return run(m,m2);
    }

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

  };


  class LobynessMeasureCirc : public Process
  {
  public:
    LobynessMeasureCirc(const Process& process) : Process(process) 
    {
	  setName("Mesh/Heat Map/Measures/Lobeyness/Circularity");
	  setDesc("Lobeyness AddOn \n"
	  "Circularity (a.k.a. Dissection Index or Roundness):\n"
    "(cell-perimeter)^2 / (4.0*PI*(cell-area)). If used please cite Sapala et al., eLife 2018 (doi: 10.7554/eLife.32794).");
	  setIcon(QIcon(":/images/LobynessPlugin.png"));			
	}

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

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

      return run(m,m2);
    }

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

  };


  class LobynessMeasureLES : public Process
  {
  public:
    LobynessMeasureLES(const Process& process) : Process(process) 
    {
	  setName("Mesh/Shape Analysis/Select Largest Empty Space");
	  setDesc("Lobeyness Plugin \n"
	  "Select the vertices belonging to LES, based on values given by csv file. \n"
    "If used please cite Sapala et al., eLife 2018 (doi: 10.7554/eLife.32794).");
      setIcon(QIcon(":/images/LobynessPlugin.png"));
	
	}

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

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

      return run(m,m2);
    }

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

  };

  class LobynessMeasureSkel : public Process
  {
  public:
    LobynessMeasureSkel(const Process& process) : Process(process) 
    {
	  setName("Mesh/Heat Map/Measures/Lobeyness/Skeleticity");
	  setDesc("Lobeyness AddOn\n"
                "Skeleticity: Largest empty space (LES) ratio of the contour and its convex hull.  If used please cite Sapala et al., eLife 2018 (doi: 10.7554/eLife.32794)");
      setIcon(QIcon(":/images/LobynessPlugin.png"));			
	}

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

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

      return run(m,m2);
    }

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

  };


  class LobynessMeasureLESOutput: public Process
  {
  public:
    LobynessMeasureLESOutput(const Process& process) : Process(process) 
    {
	    setName("Mesh/Shape Analysis/Output Largest Empty Space");
	    setDesc("Lobeyness AddOn \n"
	            "Convex(Perimeter): Ratio of cell-perimeter over that of its convex hull.\n"
              "Takes a value of 1 for convex shapes, and increases with the complexity of the contour. \n"
	            "If used please cite Sapala et al., eLife 2018 (doi: 10.7554/eLife.32794).");	
	    setIcon(QIcon(":/images/LobynessPlugin.png"));			
	  }
    bool initialize(QWidget* proc_parent)
    {
	    parent = proc_parent;
	    if(/*LEC_CSV.isEmpty() and*/ parent)
	      LEC_CSV = QFileDialog::getSaveFileName(parent, "Choose spreadsheet file to save", QDir::currentPath(),
                                              "CSV files (*.csv)");
	
	    if(LEC_CSV.isEmpty())
	      return false;
	    if(!LEC_CSV.endsWith(".csv", Qt::CaseInsensitive))
      		LEC_CSV += ".csv";

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

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

      return run(m,m2);
    }

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

    QWidget *parent;
    QString LEC_CSV;
  };


  class LobynessMeasureLESSelect: public Process
  {
  public:
    LobynessMeasureLESSelect(const Process& process) : Process(process) 
    {
	    setName("Mesh/Shape Analysis/Select Largest Empty Space");
	    setDesc("Lobeyness Plugin \n"
	            "Select the vertices belonging to LES, based on values given by csv file. \n"
	            "If used please cite Sapala et al., eLife 2018 (doi: 10.7554/eLife.32794).");	
	    setIcon(QIcon(":/images/LobynessPlugin.png"));		
	  }

    bool initialize(QWidget* parent)
    {
  	 if(parent)
  	    LEC_CSV = QFileDialog::getOpenFileName(parent, "Choose spreadsheet file to save", QDir::currentPath(),
                                              "CSV files (*.csv)");
  	  if(LEC_CSV.isEmpty())
  	    return false;
  	  if(!LEC_CSV.endsWith(".csv", Qt::CaseInsensitive))
      		LEC_CSV += ".csv";

      return true;
    }

    bool run()
    {
      Mesh *m = currentMesh();
			if(m->meshType() == "MGXC" or m->meshType() == "MGX2D")
        setErrorMessage(QString("Process can run only on MGXM or MGX3D mesh types"));
      return run(m);
    }

    bool run(Mesh *m);

    QWidget *parent;
    QString LEC_CSV;
  };

  class MeasureRectangularity : public Process
  {
  public:
    MeasureRectangularity(const Process& process) : Process(process) 
    {
	    setName("Mesh/Heat Map/Measures/Lobeyness/Rectangularity");
	    setDesc("Rectangularity: Ratio of cell area and the area of the minimum rectangle that can contain the cell.");
	    setIcon(QIcon(":/images/LobynessPlugin.png"));
	  }

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

       return run(m, m->labelHeat());
    }

    bool run(Mesh *m, IntFloatAttr& heatMap);
  };


}
#endif
