//
// This file is part of 3DCellAtlas. 
// Copyright (C) 2015 George W. Bassel and collaborators.
//
// If you use 3DCellAtlas in your work, please cite:
//   http://dx.doi.org/10.1105/tpc.15.00175
//
// 3DCellAtlas is an AddOn for MorphoGraphX - http://www.MorphoGraphX.org
// Copyright (C) 2012-2015 Richard S. Smith and collaborators.
//
// 3DCellAtlas and MorphoGraphX are free software, and are licensed under under the terms of the 
// GNU General (GPL) Public License version 2.0, http://www.gnu.org/licenses.
// 
#ifndef RootCellAnalyzing_H
#define RootCellAnalyzing_H

#include <CellAtlasConfig.hpp>

#include "RootCellProcessing.hpp"
#include <math.h>
#include "Progress.hpp"
#include <Triangulate.hpp>

#define PI 3.14159265

using namespace std;

namespace mgx {

class CellAtlas_EXPORT RootCellAnalyzing 
{
public:

  CellAtlasAttr *data;
  CellAtlasConfigAttr *config;

  std::map<int,double> arclengths;
  std::map<int,double> arclengthsUM;
  std::map<int,double> arclengthsMinUM;
  std::map<int,double> arclengthsMaxUM;
  std::map<int,Point3d> diffBezInterp;
  std::map<int,Point3d> bezInterp;
  std::map<int,double> azimCoord, radialDis, scaledRadialDis, outsideWallArea, nrNeighbors;

  double bezLength;

  std::map<int,Point3d> cellCentroids;
  std::vector<int> uniqueLabels;

  std::map<int,double> lengthLong, lengthRad, lengthCirc;
  std::map<int, Point3d> dirRad, dirLong, dirCirc;

  int firstLabel;
  Point3d firstLong;
  Point3d firstRad;

  static const int radRes = 200;
  std::map<std::pair<int,int>, double> surfaceMap; // map of binned [bezIdx,circumferential] to distance of surface

  RootCellAnalyzing(CellAtlasAttr *cellAtlasAttr, CellAtlasConfigAttr *cellAtlasConfigAttr): data(cellAtlasAttr), config(cellAtlasConfigAttr) {}

  bool analyzeCells(std::vector<int>& uniqueLabels, const vvGraph& segmentedMesh, const vvGraph& surfaceMesh, std::map<int, Point3d>& bMap, 
  std::map<int, Point3d>& diffbMap, labelVertexMap& lvMap, RootCellProcessing& rcp, int firstLabel, double minVolume);

  void analyzeBezierGrid(std::vector<int>& uniqueLabels, std::map<int,Point3d>& cellCentroids,
   std::vector<std::vector<Point3d> >& bezier, std::map<int,Point3d>& nearestPoints, std::map<int,Point2i>& nearestIndex);

  void analyzeSurface(std::vector<int>& uniqueLabels, std::map<int,Point3d>& cellCentroids, const vvGraph& surfaceMesh,
    std::map<int,Point3d>& nearestSurfacePoints);
  

  bool createCartesianCoordSystem(RootCellProcessing& rcp);

  bool createLayerCoordSystem(const vvGraph& surfaceMesh, std::vector<std::vector<Point3d> >& bezier, RootCellProcessing& rcp);

  bool createRootCoordSystem(const vvGraph& surfaceMesh, std::map<int, Point3d>& bMap, 
  std::map<int, Point3d>& diffbMap, RootCellProcessing& rcp);

  // analyzes the root based on: Bezier, segmented mesh and surface mesh
  // creates multiple outputs:
  // cellTriangles: map of label -> unique triangles
  // cellCentroids: center of all cells
  bool analyzeEmbryo(std::vector<int>& uniqueLabels, const vvGraph& segmentedMesh, 
    const vvGraph& surfaceMesh, std::vector<std::vector<Point3d> >& bezGrid, std::map<int, Point3d>& bMap, std::map<int, Point3d>& diffbMap, labelVertexMap& lvMap, 
    RootCellProcessing& rootDataBox, int firstLabel, double minVolume, QString mode);

  bool generateLabelTriangleMap(int numCells, std::vector<int>& uniqueLabels, const vvGraph& segmentedMesh, labelVertexMap& lvMap, std::map<int, triVector>& cellTriangles);

  // calculates an interpolated bezier and the longitudianl ("arclengths") coordinates of the cells
  void analyzeBezierLine(std::vector<int>& uniqueLabels, std::map<int, Point3d>& cellCentroids, std::map<int, Point3d>& bMap, std::map<int, Point3d>& diffbMap, bool flipBezier = false);

  // calc distance of each cell to surface mesh
  bool analyzeCellCalcDisSurface(RootCellProcessing& rcp, const vvGraph& surfaceMesh, bool checkArc, std::map<int, double>& surfaceArclengths,
   std::map<int, double>& surfaceRadialDis, std::map<int, Point3d>& bMap, bool considerOrientation = false);

  // calc centroids and volume, mark bad cells
  bool analyzeCellCentroidsVolumes(std::map<int, triVector>& cellTriangles, RootCellProcessing& rcp, double minVolume);

  // 2D version, calc centroids and volume (now area), mark bad cells
  bool analyzeCellCentroids2D(std::map<int, triVector>& cellTriangles, RootCellProcessing& rcp);

  void calcCircumferentialEqual(std::vector<int>& uniqueLabels, std::map<int, Point3d>& cellCentroids);

  // write data to rcp
  void writeDataFields(RootCellProcessing& rcp);

  void calcSimpleRadial(std::vector<int>& uniqueLabels, RootCellProcessing& rcp, std::map<int, Point3d>& cellCentroids);

  void calcCoordSystemCartesian(std::vector<int>& uniqueLabels);

  void calcCoordSystem(std::vector<int>& uniqueLabels, std::map<int,Point3d>& diffBezInterp, std::map<int,Point3d>& dirRad, std::map<int,Point3d>& dirLong, std::map<int,Point3d>& dirCirc);
  

  void createCoordSystem(std::vector<Point3d>& points, RootCellProcessing& rcp, std::map<int, Point3d>& bMap,
                                            std::map<int, Point3d>& diffbMap, std::map<int, Point3d>& bMapRadRef);

  void minMaxToBezierPerCell(vvGraph& S, std::vector<int>& uniqueLabels, std::map<int, Point3d>& bMap, bool flipBezier = false);

  Point3d organToCartesianCoords(Point3d organCoords, primDataStructure& p1);//, triVector& meshTris, std::map<int, Point3d>& bMap, std::map<int, Point3d>& diffbMap, 
                                // std::map<int, Point3d>& bMapRadRef);


  Point3d cartesianToOrganCoords(Point3d cartesianCoords, primDataStructure p1);//std::map<int, Point3d>& bMap, std::map<int, Point3d>& diffbMap, std::map<int, Point3d>& bMapRadRef);
  
  bool generateMeshTriangleVector(const vvGraph& S, triVector& triVec);

  Point3d organToCartesianCoordsWithMap(Point3d organCoords, primDataStructure& p1);//triVector& meshTris, std::map<int, Point3d>& bMap, 
   // std::map<int, Point3d>& diffbMap, std::map<int, Point3d>& bMapRadRef);
  

  void generateSurfaceMap(primDataStructure& p1);//std::map<int, Point3d>& bMap, triVector& triVec, std::map<int, Point3d>& diffbMap, std::map<int, Point3d>& bMapRadRef);

  void estimateAllCellLengths(RootCellProcessing& rcp, std::map<int, triVector>& cellTriangles);

private:
  std::map<int, bool> badCells;
};

}

#endif
