#ifndef DivisionAnalysisData_H
#define DivisionAnalysisData_H

#include <queue>          // std::queue

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

#include <CellTissue.hpp>

#include <CellDivision.hpp>

#include <DivisionAnalysisUtils.hpp>

using namespace std;

namespace mgx 
{


  class DivisionPlane
  {
  public:
    std::vector<vertex> vtxs;
    std::vector<Point3i> tris;

    double size;
    Point3d planeNrml;
    Point3d planePos;
    int label;

    DivisionPlane(){};
    DivisionPlane(double size, Point3d planeNrml, Point3d planePos, int label) : size(size),planeNrml(planeNrml),planePos(planePos),label(label) {}

  };

  // stores general information that might be required for running the div analysis
  struct DivisionAnalysisConfig
  {
  public:
    std::map<int, Point3d> cellNormalMap;
  };

  // stores the information about a simulated division
  class DivisionAnalysisData
  {
  public:
    std::map<int, Point3d> cellNormalMap;

    std::map<cell, double> cellBetwMap;
    std::map<cell, double> cellCurrentFlowMap;
    
    DivisionPlane divPlaneFlat;
    DivisionPlane divPlaneShortest;
    DivisionPlane divPlaneShortestBest;
    DivisionPlane divPlaneShortestBestRW;

    std::vector<CellDivision> divData;

    DivisionPlane optimalPlane;

    std::vector<DivisionPlane> customPlanes;

  };


 /**
   * \class MakeMeshCircle CellMakerProcesses2D.hpp <CellMakerProcesses2D.hpp>
   * create an unlabelled mesh with a regular polygonal shape
   *
   * MakeMeshCircle
   */
class MakeMeshCircleDA : public Process
  {
    public:
      MakeMeshCircleDA(const Process& process) : Process(process) 
      {
	    setName("Mesh/Division Analysis/Tools/Circular Mesh");
		setDesc("Creates an unlabelled mesh of a regular 2D polygon with the specified number of corners, size and triangulation. \n"
		"The generated corner points are on an ellipse with the specified radii. \n"
		"Special Mode with Corner Points=4: Creates a Rectangular Mesh with the specified side length");
		setIcon(QIcon(":/images/CellDivide.png"));

		addParm("Radius X","Radius of ellipse in x dimension","100.0");
		addParm("Radius Y","Radius of ellipse in y dimension","100.0");	
		addParm("Corner Points","Number of corner points (linearization in between)","100");
		addParm("Segment","360 for a full shape","360");
		addParm("Shift X","Translation in X dimension (microns)","0");
		addParm("Shift Y","Translation in Y dimension (microns)","0");
		addParm("Keep Mesh","Keep the existing mesh","No",booleanChoice());	  
		  }

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

        bool addEdgePoints = true;

        double triSize = 10.0;

        bool res = run(mesh, parm("Radius X").toDouble(), parm("Radius Y").toDouble(), parm("Corner Points").toInt(), 
          parm("Segment").toDouble(), parm("Shift X").toDouble(), parm("Shift Y").toDouble(), parm("Keep Mesh").toDouble(), triSize, addEdgePoints);
        if(res)
          mesh->setShowMesh();
        return res;
      }
      bool run(Mesh *mesh, double radiusX, double radiusY, int edgePoints, double meshAngle, 
        double shiftX, double shiftY, bool keep, double triSize, bool addEdgePoints);

  };

 /**
   * \class MakeMeshTetraDA CellMakerProcesses2D.hpp <CellMakerProcesses2D.hpp>
   * create an unlabelled mesh with a regular polygonal shape
   *
   * MakeMeshTetraDA
   */
class MakeMeshTetraDA : public Process
  {
    public:
      MakeMeshTetraDA(const Process& process) : Process(process) 
      {
      setName("Mesh/Division Analysis/Tools/Tetrahedra Mesh");
    setDesc("Tetra Creation");
    setIcon(QIcon(":/images/CellDivide.png"));

    addParm("Length","Length","100.0");
    addParm("Tri Size","Tri Size","1.0");
    addParm("Keep Mesh","Keep the existing mesh","No",booleanChoice());   
      }

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

        bool addEdgePoints = true;

        bool res = run(mesh, parm("Length").toDouble(), parm("Tri Size").toDouble(), stringToBool(parm("Keep Mesh")), addEdgePoints);

        // bool res = run(mesh, parm("Radius X").toDouble(), parm("Radius Y").toDouble(), parm("Corner Points").toInt(), 
        //   parm("Segment").toDouble(), parm("Shift X").toDouble(), parm("Shift Y").toDouble(), stringToBool(parm("Shift Y")), parm("Keep Mesh").toDouble(), addEdgePoints);
        if(res)
          mesh->setShowMesh();
        return res;
      }
      bool run(Mesh *mesh, double length, double triSize, bool keep, bool addEdgePoints);

  };


  

 void drawPlane(const Process& process, Mesh *m, DivisionPlane& vtxTris, Point3d scalingNormal = Point3d(0,0,0), double scalingFac = 1);

  

  //void simulateDivision2D(const vvGraph& S, const vertex& vc, std::vector<P3dDouble>& samplingDirections, Point3d& shortestPlaneNrml, double& shortesPlaneDispl);

  void simulateDivision2D(std::vector<vertex>& cellContour, Point3d& divP, Point3d divNormal, std::vector<P3dDouble>& samplingDirections, std::vector<CellDivision>& allDivisions,
  Point3d& shortestPlaneNrml, double& shortesPlaneDispl);


  
}

#endif
