//
// 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 CELL_ATLAS_HPP
#define CELL_ATLAS_HPP

#include <CellAtlasConfig.hpp>

#include <Process.hpp>
#include <BezierProcess.hpp>
#include "RootCellAnalyzing.hpp"
#include "RootCellProcessing.hpp"
#include "DataAnalyzer.hpp"
#include "ui_CellAtlasGUI.h"

#include <MeshProcessCellAnalysis3D.hpp>

namespace mgx {

//  static QDialog* dlg;

  void discretizeBezierLineRef(CuttingSurface* cutSurf, double vPos, double vRefPos, Matrix4d rotMat, int bezMapSize,
  std::map<int, Point3d>& bMap, std::map<int, Point3d>& diffbMap, std::map<int, Point3d>& refbMap);

  // TODO openmpMode not implemented yet
  class CellAtlas_EXPORT AnalyzeCells : public Process
  {
    public:
      AnalyzeCells(const Process& process) : Process(process)
      {

    setName("Mesh/Cell Atlas 3D/Root/A Analyze Cells 3D");
    setDesc("Analyze the 3D properties of the cells according to the Bezier-coordinate system. \n"
    "This process is needed for most of the other processes as it provides the raw data. \n"
    "Requirements: \n"
    "- Bezier line through the root body \n"
    "- A selected first cell for defining the orientation \n"
    "Mode: Specifies which coordiante system will be used: \n"
    "Root : Bezier Line \n"
    "Layer: Bezier Grid \n"
    "Cartesian: Cartesian XYZ");
    setIcon(QIcon(":/images/CellGraph3D.png"));

    addParm("Volume Threshold","","0");
    addParm("Mode","","Root", QStringList() << "Root" << "Layer" << "Cartesian");
  }

     bool run()
     {
        const Stack *s1 = stack(0);
        const Stack *s2 = stack(1);
        Mesh *m1 = mesh(0);
        Mesh *m2 = mesh(1);
        return run(s1, s2, m1, m2, parm("Volume Threshold").toDouble(), parm("Mode"), false /*stringToBool(parms[2])*/);
     }

     bool run(const Stack *s1, const Stack *s2, Mesh *m1, Mesh *m2, double minVolume, QString mode, bool openmpMode);
  };

 /**
   * \class SelectBadCells CellAtlas.hpp <CellAtlas.hpp>
   *
   * selects all cells that are labelled as bad (=too small, no centroid or no intersect found)
   */
  class CellAtlas_EXPORT SelectBadCells : public Process
  {
    public:
      SelectBadCells(const Process& process) : Process(process)
      {
    setName("Mesh/Cell Atlas 3D/Root/Tools/Select Bad Cells");
    setDesc("Select cells that were labelled as bad by the Analyze Cells 3D process. \n"
    "Bad cells are: \n"
    "- Cells that have a small volume (threshold in Analyze Cells 3D) \n"
    "- Cells that have an invalid centroid and/or cell size");
    setIcon(QIcon(":/images/CellAtlas.png"));
    }

      bool run()
      {
        Mesh *m1 = mesh(0);
        return run(m1);
      }

      bool run(Mesh *m1);

  };

 /**
   * \class SelectBadCells CellAtlas.hpp <CellAtlas.hpp>
   *
   * finds cells with certain properties
   */
  class CellAtlas_EXPORT CellAtlasFindCells : public Process
  {
    public:
      CellAtlasFindCells(const Process& process) : Process(process)
      {
    setName("Mesh/Cell Atlas 3D/Tools/Find Cells");
    setDesc("TBD");
    setIcon(QIcon(":/images/CellAtlas.png"));

    addParm("Volume Lower","","0");
    addParm("Any Size Lower","","0");
    addParm("Volume Cell Wall Ratio Lower","","0");
    addParm("Outside Cell Wall Percent Higher","","100");
    addParm("Connector","","AND", QStringList() << "AND" << "OR");
    }

      bool run()
      {
        Mesh *m1 = mesh(0);
        return run(m1, parm("Volume Lower").toDouble(), parm("Any Size Lower").toDouble(), parm("Volume Cell Wall Ratio Lower").toDouble(), parm("Outside Cell Wall Percent Higher").toDouble(), parm("Connector"));
      }

      bool run(Mesh *m1, double vol, double sizes, double volratio, double outsideArea, QString logicalCon);

  };


  class CellAtlas_EXPORT SaveCellData : public Process
  {
    public:
      SaveCellData(const Process& process) : Process(process)
      {
    setName("Mesh/Cell Atlas 3D/Root/Tools/Save Cell Data");
    setDesc("Save current data to csv-files");
    setIcon(QIcon(":/images/CellAtlasSave.png"));

    addParm("Filename","","");
    addParm("Filetype","","Root", QStringList() << "Root" << "Meristem");
    addParm("Extended File","","No",booleanChoice());
    }

      bool initialize(QWidget* parent);

      bool run(){
        return run(parm("Filename"), parm("Filetype"), stringToBool(parm("Extended File")));
      }
      bool run(QString filename, QString type, bool extended);

  };


  class CellAtlas_EXPORT SaveNeighborhoodData : public Process
  {
    public:
      SaveNeighborhoodData(const Process& process) : Process(process)
      {
    setName("Mesh/Export/Save Cell Neighborhood 3D");
    setDesc("Save a file with all cell walls, their adjacent cell labels and cell wall sizes.");
    setIcon(QIcon(":/images/CellAtlasSave.png"));

    addParm("Filename","Filename","");
    addParm("Parents","Parents","No",booleanChoice());
    }

      bool initialize(QWidget* parent);

      bool run(){

        Mesh* m = currentMesh();

        double vertexTolerance = 0.001;

        NhbdGraphInfo info1;
        neighborhoodGraph(m->graph(), vertexTolerance, info1);

        return run(m, parm("Filename"), info1, stringToBool(parm("Parents")));
      }

      bool run(Mesh* m, QString filename, NhbdGraphInfo& info1, bool parentLabels);

  };


  class CellAtlas_EXPORT SaveNeighborhoodData2D : public Process
  {
    public:
      SaveNeighborhoodData2D(const Process& process) : Process(process)
      {
    setName("Mesh/Export/Save Cell Neighborhood 2D");
    setDesc("Save a file with all cell walls, their adjacent cell labels and cell wall sizes.");
    setIcon(QIcon(":/images/CellAtlasSave.png"));

    addParm("Filename","Filename","");
    addParm("Parents","Parents","No",booleanChoice());
    addParm("Save","Save","Active Mesh",QStringList() << "Active Mesh" << "Other Mesh" << "Change Map");

      }

      bool initialize(QWidget* parent);

      bool run(){

        Mesh* m = currentMesh();

        Mesh* m2 = otherMesh();

        return run(m, m2, parm("Filename"), stringToBool(parm("Parents")));
      }

      bool run(Mesh* m, Mesh* m2, QString filename, bool parents);

  };

  class CellAtlas_EXPORT DisplayCellData : public Process
  {
    public:
      DisplayCellData(const Process& process) : Process(process)
      {
    setName("Mesh/Cell Atlas 3D/Root/Tools/Heatmap Cell Data");
    setDesc("Display Cell Data \n"
    "Displays cell properties as a heatmap. \n"
    "Requires analyzed or loaded data in the memory.");
    setIcon(QIcon(":/images/CellAtlas.png"));

    addParm("Data","","Longitudinal (Arclengths)", QStringList() << "Longitudinal (Arclengths)" << "Longitudinal (Absolute)" << "Circumferential Angle" << "Radial" << "Radial (Absolute)" << "Longitudinal Cell Length" << "Circumferential Cell Length" << "Radial Cell Length" << "Nr of Neighbors" << "Volume" << "Cell Wall Area" << "Outside Wall Area" << "Outside Wall Area percent" << "Associated Cortical Cell" << "Long Min" << "Long Max");  // 0
    addParm("Stack","","1");


      }

      bool run(){

        Mesh *m1 = mesh(0);
        if(parm("Data").toInt() == 2)
          m1 = mesh(1);

        return run(m1, parm("Data"));
      }

      bool run(Mesh* m1, QString choice);

  };

  class CellAtlas_EXPORT LoadCellData : public Process
  {
    public:
      LoadCellData(const Process& process) : Process(process)
      {
    setName("Mesh/Cell Atlas 3D/Root/Tools/Load Cell Data");
    setDesc("Load Cell Data \n"
    "Loads data from an analyzed root from csv files");
    setIcon(QIcon(":/images/CellAtlasLoad.png"));

    addParm("Filename","","");
    addParm("File Type","","All", QStringList() << "All" << "Data" << "Config" << "Neighborhood");
    }

      bool initialize(QWidget *parent);

      bool run(){
        return run(parm("Filename"), parm("File Type"));
      }

      bool run(QString filename, QString type);

  };

  class CellAtlas_EXPORT TopologicalCheck : public Process
  {
    public:
      TopologicalCheck(const Process& process) : Process(process)
      {
    setName("Mesh/Cell Atlas 3D/Root/E Topological Check");
    setDesc("Topological Check \n"

    "Selects potentially mislabelled cells \n"

    "This process checks the neighborhood relation of the parent labels of the cells. \n"
    "Every parent label cell layer only allows connections to two other parent labels (the layer above and below). \n"

    "Exceptions are the columella parent label (=10) and the airbubble parent label which can be specified as a parameter. \n"
    "Those two cell types are ignored in the neighborhood graph. \n"

    "Further exceptions are the rootcap and the vasculature tissue. \n"
    "hose two cell types allow connections only to one further cell type and also have to be specified as parameters. \n"

    "he process works either on the whole root or on a selection. \n"
    "Potentially mislabelled cells will be selected according to the Error Count parameter");
    setIcon(QIcon(":/images/SubdivideTriAdapt.png"));

    addParm("Work on Selection","","No",booleanChoice());
    addParm("Threshold Volume","","10");
    addParm("Threshold Wall Area","","1");
    addParm("Root Cap Label","","7");
    addParm("Air Bubble Label","","6");
    addParm("Vasculature Label","","5");
    addParm("Error Limit","","2");
    }

      bool run(){
        return run(parm("Work on Selection"), parm("Threshold Volume").toDouble(), parm("Threshold Wall Area").toDouble(), parm("Root Cap Label").toInt(), parm("Air Bubble Label").toInt(), parm("Vasculature Label").toInt(), parm("Error Limit").toInt());
      }
      bool run(QString selection, double threshVol, double threshWallArea, int rootCapLabel, int airBubbleLabel, int vascLabel, int errors);

  };

  class CellAtlas_EXPORT CellAtlas :  public Process
  {
    Q_OBJECT
    public:

      RootCellProcessing rcp;
      CellAtlasAttr *cellAtlasAttr;
      CellAtlasConfigAttr *cellAtlasConfigAttr;
      Ui_CellAtlasDialog ui;

      CellAtlas(const Process& process) : Process(process)
      {
    setName("Mesh/Cell Atlas 3D/Root/B Assign Cell Types");
    setDesc("Assign Cell Types \n"

    "View the cell property heat map and assign parent labels to the different cell types. \n"

    "Requires a selected first cell and a last root cap cell (if required). Also requires root cell data in the memory (either loaded or from the plugin Analyze Cells 3D \n"

    "GUI options: \n"
    "- Mouseclick: creates a new cluster if no existing one is nearby \n"
    "- Drag and Drop on cluster: moves the cluster \n"
    "- Double Click on cluster: deletes the cluster");
    setIcon(QIcon(":/images/Cluster.png"));

    addParm("Has multiple segments","","Yes",booleanChoice());
    }

      virtual ~CellAtlas() { }

      bool initialize(QWidget *parent);

      bool run(){
        return run(parm("Has multiple segments"));
      }
      bool run(QString rootCap);

      protected slots:

      void setImage();
      void setDeletePosition(const QPoint& p);
      void setPosition(const QPoint& p);
      void setReleasePosition(const QPoint& p);
      void setAutoCluster();
      void changeHeatmapX(QString stringX);
      void changeHeatmapY(QString string);
      void setClusterLabel(QString label);
      void setMousePosition(const QPoint& p);
      void changeSigma(double sigma);
      void changeRootPart();
      void setPreCluster();
      void resetCluster();

      protected:
      QDialog *dlg;
      Point2d mousePos;

  };

  class CellAtlas_EXPORT CollapseBezierCellAtlas : public CollapseBezier
  {
    public:
      CollapseBezierCellAtlas(const Process& process) : CollapseBezier(process)
      {
    setName("Mesh/Cell Atlas 3D/Tools/Collapse Bezier Points");
    setDesc("Collapse the 2D Bezier gridpoints into a line (needed for Analyze Cells 3D)");
    }

  };

  class CellAtlas_EXPORT AssignColumella : public Process
  {
    public:
      AssignColumella(const Process& process) : Process(process)
      {
    setName("Mesh/Cell Atlas 3D/Root/C Assign Columella");
    setDesc("Assign Columella \n"

    "Assign the Columella Cells according to the given labels");
    setIcon(QIcon(":/images/CellAtlasCol.png"));

    addParm("Root Cap Label","Root Cap Label","7");
    addParm("Columella Label","Columella Label","10");
    addParm("Ratio Threshold","Ratio Threshold","0.4");
    addParm("Radial Threshold","Radial Threshold","0.48");
    }

      bool run(){
        return run(parm("Root Cap Label").toInt(), parm("Columella Label").toInt(), parm("Ratio Threshold").toDouble(), parm("Radial Threshold").toDouble());
      }

      bool run(int labelRootCap, int labelCol, double rat_val, double sca_val);

  };

  class CellAtlas_EXPORT AssignCorticalCells : public Process
  {
    public:
      AssignCorticalCells(const Process& process) : Process(process)
      {
    setName("Mesh/Cell Atlas 3D/Root/D Assign Cortical Cells");
    setDesc("Assign Cortical Cells \n"

    "Assign the associated cortical cells to all cells \n"

    "Requires Analyze Cells 3D, loading files is not enough!");
    setIcon(QIcon(":/images/CellAtlasCort.png"));

     addParm("Cortical Cell Label","","2");
    }

      bool run(){
        return run(parm("Cortical Cell Label").toInt());
      }

      bool run(int labelCort);

  };

  class CellAtlas_EXPORT DataAnalysis : public Process
  {
    public:
      DataAnalysis(const Process& process) : Process(process)
      {
    setName("Mesh/Cell Atlas 3D/Root/Statistics/Reporter abundance analysis 3D");
    setDesc("3D reporter abundance analysis \n"

    "Data Analysis tool. \n"

    "Requires all input data in one folder. Note that input files come in pairs: \n"
    "One file for the cell data and another for the reporter data. Files have to named in the following way for the plugin to find the file pairs: \n"
    "cell data: filename.csv \n"
    "reporter data: filename''reporter file string''.csv, where ''reporter file string'' can be specified. \n"
    "Output files will be saved in the input folder");
    setIcon(QIcon(":/images/CellAtlasCort.png"));

    addParm("Input folder","","");
    addParm("Reporter file string","","_reporter");
    addParm("Merge With File","","Yes",booleanChoice());
    addParm("Output File","","");
    addParm("Sliding Avg","","No",booleanChoice());
    addParm("Sliding Avg Window","","3");
    addParm("Upper Filter Type","","No Filter", QStringList() << "No Filter" << "Percentage" << "Value");
    addParm("Upper Filter Limit","","100");
    addParm("Lower Filter Type","","No Filter", QStringList() << "No Filter" << "Percentage" << "Value");
    addParm("Lower Filter Limit","","0");
    }

      bool initialize(QWidget* parent);

      bool run(){
        return run(parm("Input folder"), parm("Reporter file string"), parm("Merge With File"), parm("Output File"), parm("Sliding Avg"), parm("Sliding Avg Window").toDouble(), stringToBool(parm("Upper Filter Type")));
      }

      bool run(QString folderControl, QString folderTreatment,  QString folderOutput, QString outputFileType, QString avg, double window, bool perCell);

  };

  class CellAtlas_EXPORT DataAnalysisGUS : public Process
  {
    public:
      DataAnalysisGUS(const Process& process) : Process(process)
      {
    setName("Mesh/Cell Atlas 3D/Root/Statistics/Reporter abundance analysis 3D");
    setDesc("3D reporter abundance analysis \n"

    "Data Analysis tool. \n"

    "Requires all input data in one folder. Note that input files come in pairs: \n"
    "One file for the cell data and another for the reporter data. Files have to named in the following way for the plugin to find the file pairs: \n"
    "cell data: filename.csv \n"
    "reporter data: filename''reporter file string''.csv, where ''reporter file string'' can be specified. \n"
    "Output files will be saved in the input folder");
    setIcon(QIcon(":/images/CellAtlas.png"));

    addParm("Input folder","","");
    addParm("Reporter file string","","_reporter");
    addParm("Merge With File","","Yes",booleanChoice());
    addParm("Output File","","");
      addParm("Sliding Avg","","No",booleanChoice());
    addParm("Sliding Avg Window","","3");
    addParm("Upper Filter Type","","No Filter", QStringList() << "No Filter" << "Percentage" << "Value");
    addParm("Upper Filter Limit","","100");
    addParm("Lower Filter Type","","No Filter", QStringList() << "No Filter" << "Percentage" << "Value");
    addParm("Lower Filter Limit","","0");
    }

      bool initialize(QWidget* parent);

      bool run(){
        Mesh *m1 = mesh(0);
        return run(m1, parm("Input folder"), parm("Reporter file string"), parm("Merge With File"), parm("Output File"), parm("Sliding Avg"), parm("Sliding Avg Window").toDouble(), parm("Upper Filter Type"), parm("Upper Filter Limit").toDouble(), parm("Lower Filter Type"),  parm("Lower Filter Limit").toDouble());
      }

      bool run(Mesh *m1, QString folderInput, QString gusFileExt, QString mergeWithFile, QString fileToMerge, QString avg, double window, QString upperFilter, double upperFilterValue, QString lowerFilter, double lowerFilterValue);

  };

  class CellAtlas_EXPORT ExamineBadVasculature : public Process
  {
    public:
      ExamineBadVasculature(const Process& process) : Process(process)
      {
    setName("Mesh/Cell Atlas 3D/Root/F Examine Vasculature");
    setDesc("Examine all cells that are selected as wrong (use Topological check) and marked as vasculature \n"
    "in order to split them into air bubble and endoderm \n"

    "Requires selected cells from Topological Check in order to work. Hereby note the Topological Check parameters: \n"
    "If a cell/air bubble is not selected it can't be fixed with this plugin! (It might be necessary to lower the thresholds there) \n"

    "The matlab function didn't work very well (creating the function with Gaussians, looking for min to find a separation point \n"
    "between air bubbles and endoderm), so another function was implemented which bases the separation point on the cell lengths \n"
    "of correct endoderm cells (the Average Separation parameter 1 choses this, 0 will use the matlab function)");
    setIcon(QIcon(":/images/CellAtlasVasc.png"));

    addParm("Vasculature Label","The parent label of the vasculature","5");
    addParm("Air Bubble Label","The parent label of air bubbles","6");
    addParm("Endo Label","The parent label of the endoderm","4");
    addParm("Average Separation","Average Separation","1");
    addParm("Sigma","Sigma","4.5");
    }

      bool run(){
        Mesh *m1 = mesh(0);
        return run(m1, parm("Vasculature Label").toInt(), parm("Air Bubble Label").toInt(), parm("Endo Label").toInt(), parm("Average Separation").toInt(), parm("Sigma").toDouble());
      }

      bool run(Mesh *m1, int labelVasc, int labelAirBubble, int labelEndo, int methodAvg, double sigma);

  };

  class CellAtlas_EXPORT DisplayShortestPath : public Process
  {
    public:
      DisplayShortestPath(const Process& process) : Process(process)
      {
    setName("Mesh/Cell Atlas 3D/Tools/Display Shortest Path");
    setDesc("Calculates the shortest path between two selected cells.");
    setIcon(QIcon(":/images/CellAtlas.png"));

    addParm("Min Wall Area","","0.001");
    addParm("Distance Measure","","Euclidian", QStringList() << "Euclidian" << "Cells");
    }
      bool run(){
        Mesh *m1 = currentMesh();
        return run(m1, parm("Min Wall Area").toDouble(), parm("Distance Measure"));
      }

      bool run(Mesh *m1, double wallThreshold, QString weight);

  };

  class CellAtlas_EXPORT WriteGrowthMapData : public Process
  {
  public:
    WriteGrowthMapData(const Process& process) : Process(process)
    {
    setName("Mesh/Cell Atlas 3D/Tools/Write Growth Map Data File");
    setDesc("Write Growth Map Data File");
    setIcon(QIcon(":/images/CellAtlas.png"));

    addParm("Filename","Filename","");


    }
    bool processParms();

    bool initialize(QWidget* parent);

    bool run()
    {
      Mesh *m = currentMesh();
      return run(m, parm("Filename"));
    }

    bool run(Mesh* m, QString filename);

  };

  class CellAtlas_EXPORT LabelledStackComparison : public Process
  {
  public:
    LabelledStackComparison(const Process& process) : Process(process)
    {
    setName("Stack/Segmentation/Compare Labelled Stacks");
    setDesc("Find the cell centers of Stack 1 and the label at their position in Stack 2");
    setIcon(QIcon(":/images/CellAtlas.png"));

    addParm("Filename","Filename","");
    addParm("Overwrite Labels Stack 2","Overwrite Labels Stack 2","No",booleanChoice());
    }
    bool processParms();

    bool initialize(QWidget* parent);

    bool run()
    {
      Stack* s1 = stack(0);
      Stack* s2 = stack(1);
      return run(s1, s2, parm("Filename"), stringToBool(parm("Overwrite Labels Stack 2")));
    }

    bool run(Stack* s1, Stack* s2, QString filename, bool overwrite);

  };


  class CellAtlas_EXPORT CellAnalysis3D : public Process
  {
  public:
    CellAnalysis3D(const Process& process) : Process(process)
    {
    setName("Mesh/Heat Map/Analysis/Cell Analysis 3D");
    setDesc("Calculates basic geometric properties of 3D cells. Visualize them using the Measure processes.");
    setIcon(QIcon(":/images/MakeHeatMap.png"));

    addParm("Use Surface Mesh","Use Surface Mesh","No", booleanChoice());  // 0
  }

    bool run()
    {
      Mesh* m = currentMesh();
      Mesh* m2 = otherMesh();
      return run(m, m2, stringToBool(parm("Use Surface Mesh")));
    }

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

    RootCellProcessing rcp;
    labelVertexMap lvMap;
    std::map<int, triVector> cellTriangles;
    std::vector<int> uniqueLabels;

  };

  class CellAtlas_EXPORT GrowthAnalysis3D : public Process
    {
    public:
      GrowthAnalysis3D(const Process& process) : Process(process)
      {
      setName("Mesh/Heat Map/Analysis/Growth Analysis 3D");
      setDesc("TBD");
      setIcon(QIcon(":/images/MakeHeatMap.png"));

      addParm("Name Prefix (optional)","Name Prefix (optional)","");
      addParm("Name Time Point 1","Name Time Point 1","T1");
      addParm("Name Time Point 2","Name Time Point 2","T2");
    }

      bool run()
      {
        Mesh* m = currentMesh();
        Mesh* m2 = otherMesh();
        return run(m, m2, parm("Name Prefix (optional)"), parm("Name Time Point 1"), parm("Name Time Point 2"));
      }

      bool run(Mesh* m, Mesh* m2, QString name, QString t1, QString t2);

    };

  /**
   * \class SmoothCellBorder
   *
   * TODO
   
  class CellAtlas_EXPORT SmoothCellBorder : public Process {
  public:
    SmoothCellBorder(const Process& proc) : Process(proc)
    {
    setName("Mesh/Test/Smooth Cell Border");
    setDesc("TODO");
    setIcon(QIcon(":/images/open.png"));

    addParm("Point Dis","Point Dis","1.0");
  }

    bool run()
    {
      Mesh* m = currentMesh();
      return run(m, parm("Point Dis").toDouble());
    }

    bool run(Mesh* m, double pointDis);

  };
*/

  /**
   * \class BezierFromCellFile
   *
   * TODO
   */
  class CellAtlas_EXPORT BezierFromCellFile : public Process {
  public:
    BezierFromCellFile(const Process& proc) : Process(proc)
    {
      setName("Misc/Bezier/Bezier From Cell File");
      setDesc("Create a Bezier line from a cell file of selected 3D cells (cells are not allowed to have more than 2 neighbors)");
      setIcon(QIcon(":/images/open.png"));

      addParm("Max Points","Max Points","20");
    }

    bool run()
    {
      Mesh* m = currentMesh();
      return run(m, parm("Max Points").toInt());
    }

    bool run(Mesh* m, int maxP);
  };


  /**
   * \class BezierFromCellFile
   *
   * TODO
   */
  class CellAtlas_EXPORT SurfaceAnalyzeBezierLine : public Process {
  public:
    SurfaceAnalyzeBezierLine(const Process& proc) : Process(proc)
    {
    setName("Mesh/Cell Atlas 3D/Surface/Analyze Cells Bezier Line");
    setDesc("Analyze a surface mesh with a Bezier line coordinate system");
    setIcon(QIcon(":/images/open.png"));

    addParm("Flip Bezier","Flip Bezier","No",booleanChoice());
  }

    bool run()
    {
      const Stack *s1 = currentStack();
      Mesh* m1 = currentMesh();
      return run(s1, m1, stringToBool(parm("Flip Bezier")));
    }

    bool run(const Stack* s1, Mesh* m1, bool flipBezier);

  };

// class CellAtlas_EXPORT DetectSurfaceWalls : public Process
//   {
//   public:
//     DetectSurfaceWalls(const Process& process) : Process(process) {}

//     bool run(const QStringList &parms)
//     {
//       Mesh* m = currentMesh();
//       Mesh* m2 = mesh(1);
//       if(m == m2) m2 = mesh(0);
//       return run(m, m2, parms[0].toDouble(), parms[1]);
//     }
//     bool run(Mesh* m, Mesh* m2, double maxDis, QString mode);

//     QString name() const { return "Mesh/Cell Atlas 3D/Tools/Detect Surface Walls"; }
//     QString description() const { return ""; }
//     QStringList parmNames() const { return QStringList() << "Max Distance" << "Output"; }
//     QStringList parmDescs() const { return QStringList() << "Max Distance" << "Output"; }
//     QStringList parmDefaults() const { return QStringList() << "5.0" << "Label Cells"; }
//     QIcon icon() const { return QIcon(":/images/MakeHeatMap.png"); }

//     ParmChoiceMap parmChoice() const
//       {
//         ParmChoiceMap map;
//         map[1] = QStringList() << "Label Cells" << "Label Walls";
//         return map;
//       }


//   };



}

#endif
