#ifndef DivisionAnalysisUtils_H
#define DivisionAnalysisUtils_H

#include <Process.hpp> // for vvGraph, forall, ...
#include <GraphUtils.hpp> // for neighborhoodGraph
#include <GraphAlgorithms.hpp>

//#include "DivisionAnalysisData.hpp"


using namespace std;

// DivisionAnalysisUtils contains typedefs and class-independent helper-functions for the DivisionAnalysis AddOn
namespace mgx
{

  typedef std::pair<Point3d, double> P3dDouble;

  typedef std::pair<cell, double> VtxDouble;
  typedef std::pair<std::set<cell>, std::set<cell> > CellSetPair;
  typedef std::pair<cell,cell> cCPair;
  typedef std::pair<Point3d, bool> P3dboolPair;
  typedef std::pair<cell, double> cellDPair;

  typedef std::pair<Point3d, int> P3dInt;
  typedef std::pair<P3dDouble, double> P3dDD;
  typedef std::pair<Point3d,Point3d> P3dP3d;


  typedef std::pair<int, int> IntInt;
  typedef std::pair<IntInt, double> IntIntDouble;
  typedef std::pair<vertex, vertex> VtxVtx;
  typedef std::pair<VtxVtx, vertex> Tri;
  typedef std::pair<VtxVtx, vertex> Vtxvtxp;
  typedef std::pair<vertex, int> VtxInt;

 // typedef pair<IntInt, double> IID;

  //typedef Vector<3, vertex> tri;
  typedef pair<Triangle, int> TriInt;

  /**
   * write a 2D array to a (csv) file
   *
   * \param filename QString folder and name of file (make sure it is *.csv if you want a csv file)
   * \param header QString the csv file header, first line, strings separated by commas
   * \param data vector<vector<double> > the data
   *
   * \ingroup DivisionAnalysisUtils
   */
   bool writeCsvFile(QString filename, QString header, vector<vector<double> >& data);

  /**
   * return the ratio ( = bigger value divided by smaller value)
   *
   * \param value1 double
   * \param value2 double
   *
   * \ingroup DivisionAnalysisUtils
   */
   double calcRatio(double value1, double value2);

  /**
   * returns an approximation of the 3D cell size (the average of the distance of every vertex to the cell center)
   *
   * \param c cell
   *
   * \ingroup DivisionAnalysisUtils
   */
   double avgCellSizeMGX3D(const cell &c);

  /**
   * returns the area of a polygon (=division plane)
   *
   * \param cellCenter Point3d the center of the cell (of the polygon)
   * \param v std::vector<Point3d> corner points of the polygon (=division plane)
   *
   * \ingroup DivisionAnalysisUtils
   */
   double divisionPlaneArea(Point3d cellCenter, const std::vector<Point3d>& v);

  /**
   * returns the summed area of multiple polygons (=division planes)
   *
   * \param cellCenter Point3d the center of the cell (of the polygon)
   * \param vMulti std::vector<std::vector<Point3d> > vector of corner points of the polygons (=division planes)
   *
   * \ingroup DivisionAnalysisUtils
   */
   double divisionPlaneAreaMulti(Point3d cellCenter, const std::vector<std::vector<Point3d> >& vMulti);

  /**
   * returns whether a cell with the specified label exists, also provides the cell and the max Label of all cells
   *
   * \param C cellGraph the cell Graph structure
   * \param searchedCell cell for the output of the cell (if it exists)
   * \param label int input label of cell to be found
   * \param maxLabel int maximum label number of all cells
   *
   * \ingroup DivisionAnalysisUtils
   */
   bool findCell(cellGraph& C, cell& searchedCell, int label, int& maxLabel);

  /**
   * add the neighbors of c2 to the cell graph of c1
   *
   * \param C cellGraph the cell Graph structure
   * \param c1 cell target cell
   * \param c2 cell source cell
   *
   * \ingroup DivisionAnalysisUtils
   */
   void calcCellGraphJoinedCells(cellGraph &C, const cell& c1, const cell& c2);

  /**
   * copies the cell graph, used for simulating cell division (without altering the original)
   *
   * \param orig CellTissue source tissue
   * \param copy CellTissue target tissue
   *
   * \ingroup DivisionAnalysisUtils
   */
   void copyCellGraph(CellTissue &orig, CellTissue &copy);

  /**
   * returns the area of a polygon
   *
   * \param
   *
   * \ingroup DivisionAnalysisUtils
   */
   std::vector<Point3d> getSharedVtxs(const cell& c1, const cell& c2);

   std::set<Triangle> getSharedTris(const cell& c1, const cell& c2);

   double calcTriArea(std::set<Triangle>& tris);

  /**
   * returns the area of a polygon
   *
   * \param
   *
   * \ingroup DivisionAnalysisUtils
   */
   void calcJoinedCell(const cell& c1, const cell& c2, vvGraph& result, bool simple);

  /**
   * returns the area of a polygon
   *
   * \param
   *
   * \ingroup DivisionAnalysisUtils
   */
   void generateSamplingDirectionsNew(int nrPlanes, int centerVarSteps, double centerVarStepSize, std::vector<P3dDouble>& samplingDirections);
   void generateSamplingDirections(double resolution, int centerVarSteps, double centerVarStepSize, std::vector<P3dDouble>& samplingDirections);

   void generateSamplingDirections(double resolution, int centerVarSteps, double centerVarStepSize,
    Point3d perpendicularPlane, std::set<Point3d> perpPlaneNormals2, bool enforceDivThroughNeighbor,
    std::vector<P3dDouble>& samplingDirections);

void addCenterDisplacementTestCut(int centerVarSteps, double centerVarStepSize, Point3d cellCenter, Point3d perpendicularPlane,
  std::set<Point3d> perpPlaneNormals2,
  std::unordered_map<Point3d, Point3d>& wallCenter, std::unordered_map<Point3d, std::vector<Point3d> > wallPoints,
  std::vector<P3dDouble>& samplingDirections);

double calcVolume(vector<Triangle> tris);

bool planeSegmentIntersect(const Point3d &p, const Point3d &nrml, const Point3d &u1, const Point3d &u2, Point3d &intersectP);

void findNeighbTris(cellGraph& C, const cell& c, std::map<cell, std::vector<Triangle> >& neighbTris);

// creates a map of all tris of a cell to the label of the neighbor cell, -1 if theres none
void cellTriNeighbors(cellGraph& C, const cell& c, std::map<Triangle, int>& trisNeighbMap);

  // estimates the volume of a 3D body with 1 open side, open Edges need to be oriented right
double estimateVolumeOpenBody(std::vector<Triangle> tris, set<vertex>& exposedVtxs);

// estimate the volume of the non-closed daughter cells
void estimateVolumeDaughterCells(std::vector<Triangle>& trisLeft, std::vector<Triangle>& trisRight, std::vector<Triangle>& trisSplit);

 // returns the labels of all neighbors of a vertex
 //std::set<int> getNeighborLabels(vvGraph& S, const vertex& v);

   // calculates the random walk betweeness centrality for the (weighted) cell graph
   //// for the unweighted version it is enough to run it with weights.size() == 0

  void calcRandWalkBetweenness(std::map<IntInt, double>& neighborhoodWeights, std::map<int, int>& labelPosMap, std::vector<int>& orderedLabels,
                               std::map<int, double>& cellBetweenessMap, bool useGPU);


   void calcRandWalkBetweenness(cellGraph &C, std::map<IntInt, double>& weights, std::map<cell, double>& cellBetweenessMap,
      bool useGPU, bool doRevCuthill);

   void calcRandWalkBetweenness(std::map<IntInt, double>& neighborhoodGraph, std::map<int, double>& labelBetweenessMap, bool useGPU);

void separateIdenticalVtxs(const cell &c);

bool planePolygonIntersect(Point3d planeNrml, Point3d planePos, std::vector<Point3d> polygonVtxs);

std::vector<Triangle> getTrisOfCells(Mesh *m, int selectedLabel, bool parentMode);

std::vector<vertex> getVtxsOfCells(Mesh *m, int selectedLabel, bool parentMode);

std::vector<vertex> getOrRemoveCommonWall(Mesh *m, std::vector<vertex>& cellVtxs, bool removeCommon, bool includeEdgeOfShared);

std::vector<Triangle> getOrRemoveCommonWall(Mesh *mesh, std::vector<Triangle>& cellTris, bool removeCommon, bool mergeDuplicated);

typedef std::pair<Point3d, Point3d> PlaneNrmlPoint;

  void assembleCellFromWalls(vvGraph& S, std::vector<vertex> vtxs, std::vector<Point3i> tris,
    std::set<vertex> &borderVtxs, double mergingThreshold, int labelCounter, bool orientCheck);



}

#endif
