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

#include <limits>

#include <QTreeWidgetItem>
#include <SystemProcessSave.hpp>

#include <Process.hpp>
#include <Progress.hpp>
#include <Information.hpp>

#include "ui_HeatMap.h"
#include "ui_LoadHeatMap.h"
#include "ui_HeatMapNew.h"
#include "ui_HeatMapManipulate.h"

// define a version of UM here as we use it in processParms,
// and it may not yet be loaded when we access them
//#define local_UM (QString::fromWCharArray(L"\xb5m"))

class Ui_LoadHeatMap;

namespace mgx
{
  typedef std::pair<vertex, vertex> VertexPr;
  typedef std::set<vertex> Set;
  mgxBase_EXPORT void getBBox(const HVec3F& pts, Point3f* bBox);

  ///\addtogroup MeshProcess
  ///@{
  
  /**
   * \class ComputeHeatMap ProcessHeatMap.hpp <MeshProcessHeatMap.hpp>
   *
   * Compute heat map on the current mesh. For details, look at the description
   * of the various parameters.
   */
  class mgxBase_EXPORT ComputeHeatMap : public Process
  {
    Q_OBJECT
  public:
    ComputeHeatMap(const Process& process) : Process(process) 
    {
    
      setName("Mesh/Heat Map/Heat Map Classic");
      setDesc("Generate heat map for the current mesh");
      setIcon(QIcon(":/images/MakeHeatMap.png"));  
    
      addParm("Type","Area: signal/geometry on curved surfaces\n"
        "Volume: for 3D only,\n"
        "Walls: quantify signal or geometry at cell borders.","Area");
      addParm("Visualize","Geometry: cell areas or volume,\n"
        "Border signal: signal associated with cell borders, within a given distance (Border Size)\n"
        "Interior signal: total signal of a cell - border signal.","Geometry");
      addParm("FileName","Path to output file.","");
      addParm("ReportFields","Options to report the following fields in spreadsheet: Geometry, Signal, Border-Interior","Geometry");
      addParm("Man. Range","Manually define the range of the color map.","No");
      addParm("Range Min","Color map lower bound.","0");
      addParm("Range Max","Color map upper bound.","65535");
      addParm("Signal Avg","Option to normalize the signal by cell area or volume","Yes");
      addParm("Global Coord","Apply the rotation/translation of the stack to the cell center coordinates.","No");
      addParm("Polarity Type","Experimental option, attempt to determine cell signal polarity based on strength of signal on different walls.\n"
        "Cell Average: compare each wall signal to signal average,\n"
        "Wall/Min: compare wall signal to the weakest wall signal in the cell.","None");
      addParm("Change Map","Compare two meshes with each other (deformation or change in signal).","No");
      addParm("Increasing","Increasing: the current mesh is the reference (T0), the other mesh is the changed state (T1),\n"
        "Decreasing: the other mesh is the reference (T0), the current mesh is the changed state (T1).",
        "Increasing", QStringList() <<"Increasing" << "Decreasing");
      addParm("Diff Type","Ratio: area or signal in changed state(T1) / area or signal in reference (T0),\n"
        "Difference: area or signal in changed state(T1) - area or signal in reference (T0),"
        "Growth: (Ratio -1)  / growth time.","Ratio", QStringList() << "Ration" <<"Grwoth"<< "Difference");
      addParm("Growth Time","Time interval between the two samples.",".001");
      addParm("Border Size(µm)","Border Size in µm","1.0");

  }

    bool run();

    enum MapType { AREA, VOLUME, WALL };

    enum SignalType {
      GEOMETRY = 0x1,
      BORDER = 0x2,
      INTERIOR = 0x4,
      TOTAL = 0x8,
      BORDER_TOTAL = 0x10,
      INTERIOR_TOTAL = 0x20
    };

    enum PolarityType { NONE, CELL_AVERAGE, WALL_MIN };

    enum MultiMapType {
      SINGLE,
      INCREASING_RATIO,
      INCREASING_DIFF,
      DECREASING_RATIO,
      DECREASING_DIFF,
      INCREASING_GROWTH,
      DECREASING_GROWTH
    };

    struct ImageHeatMaps {
      IntFloatAttr LabelGeom;         // Label Areas
      IntFloatAttr LabelBordGeom;     // Area of label borders
      IntFloatAttr LabelIntGeom;      // Area of label interiors
      IntFloatAttr LabelTotalSig;     // Label total signal
      IntFloatAttr LabelBordSig;      // Label border signal
      IntFloatAttr LabelIntSig;       // Label interior signal
      IntFloatAttr LabelHeatAmt;      // Label HeatMap temp for change map
      IntHVec3uMap LabelTris;        // Label triangle lists
      IntVIdSetMap LabelPts;         // Label triangle list points

      IntIntFloatAttr WallBordGeom;     // Wall border areas
      IntIntFloatAttr WallBordSig;      // Wall border signal
      IntIntFloatAttr WallHeatAmt;      // Wall border signal amt for change map
    };

    bool run(Mesh* mesh1, Mesh* mesh2, MapType map, SignalType signal,
             const QString& reportFile, int report, bool manualRange, float rangeMin,
             float rangeMax, bool signalAverage, bool globalCoordinates, PolarityType polarity,
             MultiMapType multiMapType, float growthTime, float borderSize);

    bool initialize(QWidget* parent);

    QDialog* dlg;

    // was protected, now accessed by cell atlas
    bool getLabelMaps(ImageHeatMaps& map, Mesh* mesh);

  protected slots:
    void changeMapType(const QString& type);
    void selectSpreadsheetFile();

  protected:
    
    MapType mapType;
    SignalType signal;
    int report;
    bool manualRange;
    float rangeMin;
    float rangeMax;
    bool signalAverage;
    bool globalCoordinates;
    PolarityType polarity;
    bool changeMap;
    MultiMapType multiMapType;
    float growthTime;
    float borderSize;
  };
  
  /**
   * \class DeleteHeatRangeLabels ProcessHeatMap.hpp <MeshProcessHeatMap.hpp>
   *
   * Delete labeling, and possibly cells, if their heat falls within a defined range.
   */
  class mgxBase_EXPORT DeleteHeatRangeLabels : public Process
  {
  public:
    DeleteHeatRangeLabels(const Process& process) : Process(process) 
    {
    setName("Mesh/Heat Map/Delete Heat Range Labels");
      setDesc("Delete labels with heat within a given range. The heat is given relative to the total range.");
      setIcon(QIcon(":/images/ClearStack.png"));

      addParm("Rescale","Redefine the lower/upper bounds of colormap to fit the range of values.","Yes",booleanChoice());
      addParm("Delete cells","Delete vertices within a given range of the color map (Min Heat-Max Heat).","No",booleanChoice());
      addParm("Min Heat","Lower bound of color value for which the cells will be deleted (empty for current min).","");
      addParm("Max Heat","Upper bound of color value for which the cells will be deleted (empty for current max).","");
    }

    bool run()
    {
      if(!checkState().mesh(MESH_NON_EMPTY))
        return false;
      float min, max;
      bool ok = true;
      if(parm("Min Heat").isEmpty())
        min = std::numeric_limits<float>::quiet_NaN();
      else
        min = parm("Min Heat").toFloat(&ok);
      if(not ok)
        return setErrorMessage("Error, parameter 'Min Heat' must be either empty, or a number");
      if(parm("Max Heat").isEmpty())
        max = std::numeric_limits<float>::quiet_NaN();
      else
        max = parm("Max Heat").toFloat(&ok);
      if(not ok)
        return setErrorMessage("Error, parameter 'Min Heat' must be either empty, or a number");
      return run(currentMesh(), stringToBool(parm("Rescale")), stringToBool(parm("Delete cells")), min, max);
    }

    bool run(Mesh* mesh, bool rescale, bool deleteCells, float min, float max);
    
  };
  
  /**
   * \class RescaleHeatMap ProcessHeatMap.hpp <MeshProcessHeatMap.hpp>
   *
   * Rescale the heat map, specifying new lower and upper bound.
   */
  class mgxBase_EXPORT RescaleHeatMap : public Process
  {
  public:
    RescaleHeatMap(const Process& process) : Process(process) 
    {  
    setName("Mesh/Heat Map/Heat Map Set Range");
      setDesc("Change the range of the current heat map for display.");
      setIcon(QIcon(":/images/MakeHeatMap.png"));
      
      addParm("Min","Lower bound (empty for current)","");
      addParm("Max","Upper bound (empty for current)","");    
  }

    bool run()
    {
      if(!checkState().mesh(MESH_HEAT))
        return false;
      Mesh *mesh = currentMesh();
      bool ok = true;
      float min, max;
      if(parm("Min").isEmpty())
        min = std::numeric_limits<float>::quiet_NaN();
      else
        min = parm("Min").toFloat(&ok);
      if(not ok)
        return setErrorMessage("Error, parameter 'Min' must be either empty, or a number");
      if(parm("Max").isEmpty())
        max = std::numeric_limits<float>::quiet_NaN();
      else
        max = parm("Max").toFloat(&ok);
      if(not ok)
        return setErrorMessage("Error, parameter 'Max' must be either empty, or a number");
      return run(mesh, min, max);
    }

    bool run(Mesh* m, float min, float max);

  };
  
  /**
   * \class SaveHeatMap ProcessHeatMap.hpp <MeshProcessHeatMap.hpp>
   *
   * Save the current heat map in a CSV file.
   */
  class mgxBase_EXPORT SaveHeatMap : public Process
  {
    Q_OBJECT
  public:
    SaveHeatMap(const Process& process) : Process(process) 
    {
    setName("Mesh/Heat Map/Heat Map Save");
      setDesc("Save heat map to a file");
      setIcon(QIcon(":/images/MakeHeatMap.png"));

      addParm("Filename","Path to spreadsheat file.","");  
  }

    bool initialize(QWidget* parent);

    bool run()
    {
      if(!checkState().mesh(MESH_ANY))
        return false;
      Mesh *mesh = currentMesh();
      return run(mesh, parm("Filename"));
    }

    bool run(Mesh* mesh, const QString& filename);

  };
  
  /**
   * \class LoadHeatMap ProcessHeatMap.hpp <MeshProcessHeatMap.hpp>
   *
   * Load the heat map from a CSV file. The CSV file must have the following structure:
   *
   *  - The first column must be cell labels (the name doesn't matter)
   *  - If the heat map is on walls, the second column must be called "Neighbor"
   *    and also be cell labels.
   *  - Each other column is a potential heat. The column name is structured
   *    either as "name" or "name (unit)". In the second case, the unit will be
   *    extracted for display.
   */
  class mgxBase_EXPORT LoadHeatMap : public Process 
  {
    Q_OBJECT
  public:
    LoadHeatMap(const Process& process) : Process(process)  
    {
    setName("Mesh/Heat Map/Heat Map Load");
    setDesc("Load a heat map file and set the corresponding heat for each label");

    addParm("File Name","Path to spreadsheet file.","");
    addParm("Column","Column (name or number) of the csv file to load for display, s/b > 0, first column is label.\n", "2");
    addParm("Border size (µm)","Width of cell outline used for vizualization of 'wall' colormaps","1.0");
    setIcon(QIcon(":/images/MakeHeatMap.png"));  
  }

    bool initialize(QWidget* parent);

    bool run()
    {
      if(!checkState().mesh(MESH_ANY))
        return false;
      Mesh *mesh = currentMesh();
      return run(mesh, parm("File Name"), parm("Column").toInt(), parm("Border size (µm)").toFloat());
    }

    bool run(Mesh* mesh, const QString& filename, int column, float border_size);
    bool selectFile(const QString& filename);

  public slots:
    void selectFile();
    void selectColumn(int c);

  protected:
    QDialog *dlg;
    Ui_LoadHeatMap *ui = 0;
    QList<QString> columnUnits;
  };
  
  /**
   * \class SaveParents ProcessHeatMap.hpp <MeshProcessHeatMap.hpp>
   *
   * Store mapping from each label to a label in a different mesh (parent).
   */
  // DUPLICATED CODE?
  /* class mgxBase_EXPORT SaveParents : public Process
  {
  public:
    SaveParents(const Process& process) : Process(process) {}

    bool initialize(QStringList& parms, QWidget* parent);

    bool run(const QStringList &parms)
    {
      if(!checkState().mesh(MESH_ANY))
        return false;
      Mesh *mesh = currentMesh();
      return run(mesh, parms[0]);
    }

    bool run(Mesh* mesh, const QString& filename);

    QString name() const { return "Mesh/Lineage Tracking/Save Parents"; }
    QString description() const { return "Save map of labels to parents labels to a file"; }
    QStringList parmNames() const { return QStringList() << "Filename"; }
    QStringList parmDescs() const { return QStringList() << "Path to label parents file."; }
    QStringList parmDefaults() const { return QStringList() << ""; }
    QIcon icon() const { return QIcon(":/images/Parents.png"); }
  };
  */
  /**
   * Clear label to parent mapping
   */
/*
  class mgxBase_EXPORT ResetParents : public Process
  {
  public:
    ResetParents(const Process& process) : Process(process) {}

    bool run(const QStringList &)
    {
      if(!checkState().mesh(MESH_ANY))
        return false;
      Mesh *mesh = currentMesh();
      return run(mesh);
    }

    bool run(Mesh* mesh);

    QString name() const { return "Mesh/Lineage Tracking/Clear Parents"; }
    QString description() const { return "Clear mapping from parents to labels"; }
    QStringList parmNames() const { return QStringList(); }
    QStringList parmDescs() const { return QStringList(); }
    QIcon icon() const { return QIcon(":/images/Parents.png"); }
  };
*/

  class mgxBase_EXPORT ScaleHeatMap : public Process
  {
  public:
    ScaleHeatMap(const Process& process) : Process(process) 
    {  
    setName("Mesh/Heat Map/Operators/Heat Map Scale Values");
      setDesc("Scales the current heat map values according to the chosen type.\n"
      "log uses the base of 10. inverse of 0 is 0 \n"
      "the parameter (P) is used for: \n"
      "Pow (^P) \n"
      "Add (+P) \n"
      "Mult (*P)");
    setIcon(QIcon(":/images/MakeHeatMap.png"));  
    
      addParm("Type","Type","inverse", QStringList() << "inverse" << "square" << "sqrt" << "log" << "pow" << "ordinal" << "add" << "mult");
      addParm("Parameter","Parameter","0.0");        
  }

    bool run()
    {
      if(!checkState().mesh(MESH_ANY))
        return false;
      Mesh *mesh = currentMesh();
      return run(mesh, parm("Type"), parm("Parameter").toDouble());
    }

    bool run(Mesh* mesh, QString type, double parameter);

  };

  class mgxBase_EXPORT NormalizeHeatMap : public Process
  {
  public:
    NormalizeHeatMap(const Process& process) : Process(process) 
    {
      setName("Mesh/Heat Map/Operators/Heat Map Normalize");
      setDesc("Normalizes the current heat map values according to the chosen type.");
    setIcon(QIcon(":/images/MakeHeatMap.png"));
    
      addParm("Type","Type","Max Min", QStringList() << "Max Min" << "Selected Cells");                
  }

    bool run()
    {
      if(!checkState().mesh(MESH_ANY))
        return false;
      Mesh *mesh = currentMesh();
      return run(mesh, parm("Type"));
    }

    bool run(Mesh* mesh, QString type);

  };

  class mgxBase_EXPORT CreateAttrMap : public Process
  {
  public:
    CreateAttrMap(const Process& process) : Process(process) 
    {
    
      setName("Mesh/Heat Map/Operators/Export Heat to Attr Map");
      setDesc("Creates an Attribute map of the chosen name (preceding Measure Label Double) using the current heat map values.");
      setIcon(QIcon(":/images/MakeHeatMap.png"));    
      
      addParm("Prefix","Prefix","Measure Label Double");
      addParm("Name","Name","Custom Heat Map");  
      addParm("Key","Keys of Attr Map","Label", QStringList() << "Label" << "Parent");
      addParm("Heat Map Type","Type of Heat Map to be saved","Label Heat", QStringList() << "Label Heat" << "Parent Heat");  
      addParm("Save to","Save to which mesh","Active Mesh", QStringList() << "Active Mesh" << "Other Mesh");
      addParm("Clean","Remove entries for non-existent cells","No",booleanChoice());
  }

    bool run()
    {
      if(!checkState().mesh(MESH_ANY))
        return false;
      Mesh *m = currentMesh();
      Mesh *m2;
      if(currentMesh() == mesh(0))
        m2 = mesh(1);
      else
        m2 = mesh(0);
      return run(m, m2, parm("Prefix"), parm("Name"), parm("Key"), parm("Heat Map Type"), parm("Save to"),stringToBool(parm("Clean")));
    }

    bool run(Mesh* m, Mesh* m2, QString prefix, QString name, QString key, QString value, QString meshNumber, bool cleanMesh);

  };

//NEED TO ADD OPTION TO SPECIFY WHERE TO SAVE ALL ATTRIBUTE MAPS
  class mgxBase_EXPORT HeatMapGrowth : public Process
  {
  public:
    HeatMapGrowth(const Process& process) : Process(process) 
    {
      setName("Mesh/Heat Map/Analysis/Growth Analysis 2D");
      setDesc("Creates all attribute maps with a chosen prefix required for heat-map based growth analysis using the current heat map values.");
    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");
      addParm("Create Custom Dirs From Heat","Create Custom Dirs From Current Heat Map","No",booleanChoice());      
  }

    bool run()
    {
      if(!checkState().mesh(MESH_ANY))
        return false;
      Mesh *m = currentMesh();
      Mesh *m2;
      if(currentMesh() == mesh(0))
        m2 = mesh(1);
      else
        m2 = mesh(0);
      return run(m, m2, parm("Name Prefix (optional)"), parm("Name Time Point 1"), parm("Name Time Point 2"), stringToBool(parm("Create Custom Dirs From Heat")));
    }

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

  };

  class mgxBase_EXPORT CleanHeatMap : public Process
  {
  public:
    CleanHeatMap(const Process& process) : Process(process) 
    {
    setName("Mesh/Heat Map/Operators/Clean Heat Map");
      setDesc("Removes unwanted entries from the heat map (i.e. values for non-existent cells).");
    setIcon(QIcon(":/images/MakeHeatMap.png"));  
    
      addParm("Selection","Eliminate non-selected cells","No",booleanChoice());
      addParm("Min valid","Minimum valid value","NA");
      addParm("Max valid","Maximum valid value","NA");      
  }

    bool run()
    {
      if(!checkState().mesh(MESH_ANY))
        return false;
      Mesh *m = currentMesh();
      Mesh *m2;
      if(currentMesh() == mesh(0))
        m2 = mesh(1);
      else
        m2 = mesh(0);
      return run(m, m2, /*parm("Selection"),*/ stringToBool(parm("Selection")), parm("Min valid"), parm("Max valid"));
    }

    bool run(Mesh* m, Mesh* m2, /*QString selectedMap,*/ bool useSelection, QString minValue, QString maxValue);

  };

  class mgxBase_EXPORT CombineAttrMaps : public Process
  {
  public:
    CombineAttrMaps(const Process& process) : Process(process) 
    {
    
      setName("Mesh/Heat Map/Operators/Heat Map Transform");
      setDesc("Creates an Attribute map of the chosen name (preceding Measure Label Double) using the current heat map values.");        
      setIcon(QIcon(":/images/MakeHeatMap.png"));
      
      addParm("Attr Map 1","Attr Map 1","A1");
      addParm("Attr Map 2","Attr Map 2","A2");
      addParm("Combination","Combination","Yes");
      addParm("Combination Type","Combination Type","Addition (A + B)");
      addParm("Transformation","Transformation","No");
      addParm("Transformation Type","Transformation Type","Neighborhood");
      addParm("Transformation Type 2","Transformation Type 2","Average");
      addParm("Ignore Parent","Ignore Parent","-1");
      addParm("Lower Threshold","Lower Threshold","0");
      addParm("Upper Threshold","Upper Threshold","100000");
      addParm("New Attr Map","Combined Attr Map","Combined Attr Map");  
  }
  
    bool initialize(QWidget *parent);

    bool run()
    {
      if(!checkState().mesh(MESH_ANY))
        return false;
      Mesh *m = currentMesh();
      return run(m, parm("Attr Map 1"), parm("Attr Map 2"), 
        stringToBool(parm("Combination")), parm("Combination Type"), 
        stringToBool(parm("Transformation")), parm("Transformation Type"), parm("Transformation Type 2"), parm("Ignore Parent").toInt(), parm("Lower Threshold").toDouble(), parm("Upper Threshold").toDouble(),
        parm("New Attr Map"));
    }

    bool run(Mesh* m, QString name1, QString name2, 
      bool combination, QString combinationType, 
      bool transformation, QString transformationType, QString transformationType2, int ignoreParent, double lowerT, double upperT,
      QString combinedAttrMap);

  };

  class mgxBase_EXPORT SelectByHeat : public Process
  {
  public:
    SelectByHeat(const Process& process) : Process(process) 
    {
    setName("Mesh/Heat Map/Heat Map Select");
      setDesc("Select Cells according to their heat value.");
    setIcon(QIcon(":/images/MakeHeatMap.png"));
    
      addParm("Lower Threshold","Lower Threshold","0.0");
      addParm("Upper Threshold","Upper Threshold","1.0");
      addParm("Reset Selection","Reset Selection","Yes",booleanChoice());
    
  }

    bool run()
    {
      if(!checkState().mesh(MESH_ANY))
        return false;
      Mesh *m = currentMesh();
      return run(m, parm("Lower Threshold").toDouble(), parm("Upper Threshold").toDouble(), stringToBool(parm("Reset Selection")));
    }

    bool run(Mesh* m, double lowerThreshold, double upperThreshold, bool resetSelection);

  };

  class mgxBase_EXPORT SwitchHeatParent : public Process
  {
  public:
    SwitchHeatParent(const Process& process) : Process(process) 
    {
      setName("Mesh/Heat Map/Operators/Switch Heat and Parent");
      setDesc("Select Cells according to their heat value.");
      setIcon(QIcon(":/images/MakeHeatMap.png"));    
    
      addParm("Direction","Direction","Heat -> Parent", QStringList() << "Heat -> Parent" << "Parent -> Heat" << "Parent <-> Heat");  // 0  
    }

    bool run()
    {
      if(!checkState().mesh(MESH_ANY))
        return false;
      Mesh *m = currentMesh();
      return run(m, parm("Direction"));
    }

    bool run(Mesh* m, QString direction);

  };


  class mgxBase_EXPORT HeatAndChangeMap : public Process
  {
    Q_OBJECT
  public:
    QStringList measureProcesses;
    Ui_HeatMapDialogNew ui;
    Mesh *m;

    HeatAndChangeMap(const Process& process) : Process(process) 
    {
      setName("Mesh/Heat Map/Heat Map");
      setDesc("New Heat Map process that supports Attribute Maps used for: \n"
      "- generation of heat maps of any measure or importing attribute maps as heat maps \n"
      "- generation of change maps of any measure or attribute maps \n"
      "- generation of heat maps based on label or parent");

      addParm("Measure","Measure","");
      addParm("Force Recalc","Force Recalc","No",booleanChoice());
      addParm("Identical Label Behavior","Identical Label Behavior","Sum", QStringList() << "Sum" << "Average" << "Max"<<"Min"<<"Max-Min");
      addParm("Change Map","Change Map","No",booleanChoice());
      addParm("Change Map Dir","Change Map Dir","Increasing", QStringList() << "Increasing" << "Decreasing");
      addParm("Change Map Type","Change Map Type","Ratio", QStringList() << "Ratio" << "Difference");
      addParm("Use Labels Active Mesh","Use Labels Active Mesh","Yes",booleanChoice());
      addParm("Use Labels Inactive Mesh","Use Labels Inactive Mesh","No",booleanChoice());
       
    
      setIcon(QIcon(":/images/MakeHeatMap.png"));    
    }

    bool initialize(QWidget *parent);
    
    bool run()
    {
      if(!checkState().mesh(MESH_ANY))
        return false;
      Mesh *m = currentMesh();
      Mesh* m2;
      if(currentMesh() == mesh(0))
        m2 = mesh(1);
      else
        m2 = mesh(0);

      return run(m, m2, parm("Measure"), stringToBool(parm("Force Recalc")), parm("Identical Label Behavior"),
          stringToBool(parm("Change Map")), parm("Change Map Dir"), parm("Change Map Type")/*, stringToBool(parms[7]), parms[8].toDouble(), parms[9].toDouble()*/,
          stringToBool(parm("Use Labels Active Mesh")), stringToBool(parm("Use Labels Inactive Mesh")));
    }

    bool getData(Mesh* m, QString measureFolder, QString measure, bool forceRecalc, AttrMap<int, double>& data);

    bool run(Mesh* m, Mesh* m2, QString measure, bool forceRecalc, QString identicalLabel,
             bool changeMap, QString changeDir, QString changeType, bool m1Labels, bool m2Labels);


  public slots:
    void changeMapType(const QString& type);
  };

  class mgxBase_EXPORT RunAllParentHeat : public Process
  {
  public:
	  RunAllParentHeat(const Process& process) : Process(process) 
    {
	    setName("Mesh/Heat Map/Analysis/Parent Heats");
	    setDesc("Computes parent based heat-maps for all identical label behaviour options. Stores in attribute maps");
	    addParm("Prefix","String to prepend to attr map names","");
	    addParm("Attr map","Name of attribute map to be used for parent heats","");
	    setIcon(QIcon(":/images/MakeHeatMap.png"));  
    }
    
    bool run();
  };


  class mgxBase_EXPORT HeatMapCellType : public Process
  {
  public:
    HeatMapCellType(const Process& process) : Process(process) 
    {
      setName("Mesh/Heat Map/Operators/Heat Map Binning");
      setDesc("Use the current heat map to create a binning of based on cells or values and set the parent label according to the bin.\n"
      "In Threshold mode only two bins are created based on the parameters in the process Select By Heat");
    setIcon(QIcon(":/images/MakeHeatMap.png"));    
    
      addParm("Mode","Mode","Bins Cells", QStringList() << "Bins Cells" << "Bins Heat" << "Threshold");  
      addParm("Nr of Bins","Nr of Bins","5");
    
  }
    
    bool run()
    {
      Mesh* m = currentMesh();
      return run(m, parm("Mode"), parm("Nr of Bins").toInt());
    }
    
    bool run(Mesh* m, QString mode, int bins);

  };

  class mgxBase_EXPORT HeatMapAllMeasures2D : public Process
  { 
  public:
    HeatMapAllMeasures2D(const Process &process): Process(process) 
    {
      setName("Mesh/Heat Map/Analysis/Cell Analysis 2D");
      setDesc("Calculates ALL 2D measure maps. (Might take a bit depending on the mesh.");
      setIcon(QIcon(":/images/MakeHeatMap.png"));    
    
      addParm("Exclude Slow Measures","Exclude Slow Measures","Yes",booleanChoice());        
    }
  
    bool run(){
      return run(stringToBool(parm("Exclude Slow Measures"))); 
    }
    
    bool run(bool excludeSlow);
  };

  class mgxBase_EXPORT HeatMapSmooth : public Process
  { 
  public:
    HeatMapSmooth(const Process &process): Process(process) 
    {
      setName("Mesh/Heat Map/Heat Map Smooth");
      setDesc("Smooth the current heat map across the cell neighborhood.\n"
        "Heat values can be weighted by cell area and the result can be directly exported to an Attr Map");
      setIcon(QIcon(":/images/MakeHeatMap.png"));    
    
      addParm("Passes","Number of smoothing passes","1");
      addParm("Weights","Weights","None",QStringList() << "None" << "Area");
      addParm("Rescale Heat","Rescale Heat","Yes",booleanChoice());
      addParm("Export Attr Map Name","Export Attr Map Name","");
    }
  
    bool run(){
      Mesh* m = currentMesh();
      return run(m, parm("Passes").toInt(), parm("Weights"), stringToBool(parm("Rescale Heat")), parm("Export Attr Map Name")); 
    }
    
    bool run(Mesh* m, int passes, QString mode, bool rescale, QString attrName);

  };

  class mgxBase_EXPORT ApplyHeat : public Process 
  {
  public:
    ApplyHeat(const Process &process) : Process(process) {}
  
    bool run(const QStringList& parms)
    {
      if(!checkState().mesh(MESH_NON_EMPTY))
        return false;
      Mesh* m = currentMesh();
      return run(m, parms[0], parms[1].toDouble(), parms[2].toDouble(), parms[3].toDouble());
    }
  
    bool run(Mesh* mesh, const QString& heatUnit, double heat, double minHeat, double maxHeat);
  
    QString name() const { return "Mesh/Heat Map/Apply Heat"; }
    QString description() const { return "Apply heat to selected labels and scale the heat to range"; }
    QStringList parmNames() const { return QStringList() << "Units" << "Heat" << "Min Heat" << "Max Heat"; }
    QStringList parmDescs() const { return QStringList() 
      << "Units for the new heat." << "New heat value to apply." 
      << "Lower bound for heat." << "Upper bound for heat."; }
    QStringList parmDefaults() const { return QStringList() << "" << "1.0" << "1.0" << "10.0"; }
    QIcon icon() const { return QIcon(":/images/ApplyHeat.png"); }
  };

// class mgxBase_EXPORT HeatMapCellDistanceSignal : public Process
//   { 
//     public:
//       HeatMapCellDistanceSignal(const Process &process): Process(process) {}
//       bool run(const QStringList &parms){
//         Mesh* m = currentMesh();
//         return run(m); 
//       }
//       bool run(Mesh* m);
//       QString name() const { return "Mesh/Heat Map/Test/Cell Distance Signal"; }
//       QString description() const { return "Cell Distance Signal"; }
//       QStringList parmNames() const { return QStringList(); }
//       QStringList parmDescs() const { return QStringList(); }
//       QStringList parmDefaults() const { return QStringList();}
//       QIcon icon() const { return QIcon(":/images/MakeHeatMap.png); }
//   };

  double geoMeanVectorValue(std::vector<double>& values);

}
#endif
