//
// 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.
//

#include <math.h>       /* log10 */

#include "MeshProcessLineage.hpp"
#include "MeshProcessMeasures.hpp"
#include "Progress.hpp"
#include "GraphUtils.hpp"
#include "StackProcess.hpp" // findNeighborsLabel

namespace mgx
{
  // Eliminate from parents labels which are not in mesh1 or mesh2
  bool CorrectParents::run(Mesh* mesh1, Mesh* mesh2)
  {
    IntIntAttr &daughterParentMap = mesh2->parents();
    if(daughterParentMap.size() == 0) {
      setErrorMessage(QString("No parent labels on second mesh"));
      return false;
    }

    const vvGraph &S1 = mesh1->graph();
    const vvGraph &S2 = mesh2->graph();

    // Check the parents for cells which are not in the mesh1 or mesh2
    IntIntAttr daughterParentMaptemp;
    std::set<int> labelsS1;
    std::set<int> labelsS2;
    std::set<int> problemsS1;
    std::set<int> problemsS2;
    forall(vertex c, S1)
      if(c->label > 0)
        labelsS1.insert(c->label);
    forall(vertex c, S2)
      if(c->label > 0)
        labelsS2.insert(c->label);
    for(IntIntAttr::iterator it = daughterParentMap.begin(); it != daughterParentMap.end(); it++) {
      int lParent = it->second;
      int lDaughter = it->first;
      if(labelsS1.count(lParent) == 0)
        problemsS1.insert(lParent);
      if(labelsS2.count(lDaughter) == 0)
        problemsS2.insert(lDaughter);
      if(labelsS1.count(lParent) != 0 and labelsS2.count(lDaughter) != 0)
        daughterParentMaptemp[lDaughter] = lParent;
    }
    daughterParentMap = daughterParentMaptemp;

    if(problemsS1.size() > 0 or problemsS2.size() > 0) {
      Information::out << "problems with parents. Some labels in the map are not present in the mesh:\n"
                       << "in mesh1, the following labels do not exist:\n";
      forall(int l, problemsS1)
        Information::out << l << ", ";
      Information::out << "\nin mesh2, the following labels do not exist:\n";
      forall(int l, problemsS2)
        Information::out << l << ", ";
      Information::out << endl;
    }
    return true;
  }
  REGISTER_PROCESS(CorrectParents);

  // Make a heat map based on how many daughter cells a parent cell has
  bool HeatMapProliferation::run(Mesh* mesh, Mesh* m2, bool onOtherMesh)
  {
    vvGraph& S = mesh->graph();

    IntIntAttr parents = mesh->parents();
    if(onOtherMesh) parents = m2->parents();
    IntSet labels;

    // get list of labels
    forall(const vertex& v, S)
      if(v->label > 0)
        labels.insert(v->label);

    IntFloatAttr &labelHeat = mesh->labelHeat();
    labelHeat.clear();

    // find how many daughters each parent has
    forall(IntIntPair p, parents){
      if(p.first > 0 and p.second > 0)
        labelHeat[p.second] += 1;
    }

    mesh->setUseParents(!onOtherMesh);

    // Set the map bounds.
    mesh->heatMapBounds() = mesh->calcHeatMapBounds();
    // Unit of the heat map
    mesh->heatMapUnit() = QString("#");
    // Tell the system the triangles have changed (here, the heat map)
    mesh->updateTriangles();

    return true;
  }
  REGISTER_PROCESS(HeatMapProliferation);

  // Select all the vertices on newly divided walls
  bool MeshSelectNewWalls::run(Mesh* m)
  {
    auto parents = m->parents();
    auto &S = m->graph();

    for(const vertex &v : S) {
      if(v->label != -1)
        continue;
      IntSet labelSet, parentSet;
      forall(const vertex &n, S.neighbors(v)) { // Looks like neighors() is not compatible with C++11 for()
        if(n->label > 0) {
          labelSet.insert(n->label);
          if(parents[n->label] > 0)
            parentSet.insert(parents[n->label]);
        }
      }
      if(labelSet.size() > 1 and parentSet.size() > 0 and labelSet.size() > parentSet.size())
        v->selected = true;
    }

    return true;
  }
  REGISTER_PROCESS(MeshSelectNewWalls);

  // Make a heat map based on how many daughter cells a parent cell has
  bool HeatMapAreaAsymmetry::run(Mesh* mesh)
  {
    vvGraph& S = mesh->graph();

    IntIntAttr parents = mesh->parents();
    IntSet labels;

    // get list of labels
    forall(const vertex& v, S)
      if(v->label > 0)
        labels.insert(v->label);

    IntFloatAttr labelCount;
    IntFloatAttr labelMean;
    IntFloatAttr labelMeanAccumulate;
    IntFloatAttr labelSTDAccumulate;
    IntFloatAttr labelSTD;
    IntFloatAttr cellArea;
    MeasureArea AreaProc(*this);
    mesh->setUseParents(false);
    if(!(AreaProc.run(mesh,cellArea)))
        Information::out<<"Could not calculate cell area"<<endl;
    mesh->setUseParents(true);
    IntFloatAttr &labelHeat = mesh->labelHeat();
    labelHeat.clear();

    // find how daughters each parent has
    forall(IntIntPair p, parents)
      if(p.first > 0 and p.second > 0){
        labelMeanAccumulate[p.second] += cellArea[p.first];
        labelCount[p.second] += 1;
        labelMean[p.second] = labelMeanAccumulate[p.second]/labelCount[p.second]; //Recalculated every time, could be done more efficiently
    }

    forall(IntIntPair p, parents)
      if(p.first > 0 and p.second > 0 and labelCount[p.second]>1 ){
        labelSTDAccumulate[p.second] += pow(labelMean[p.second] - cellArea[p.first],2);
        labelSTD[p.second] = sqrt(labelSTDAccumulate[p.second]/labelCount[p.second]/labelMean[p.second]/labelMean[p.second]);
        labelHeat[p.second] = labelSTD[p.second];
        //std::cout<<labelSTDAccumulate[p.second]<<" : "<<labelMean[p.second]<<" : "<<labelCount[p.second]<<std::endl;
        //std::cout<<cellArea[p.second]<<std::endl;
        //std::cout<<labelHeat[p.second]<<std::endl;
      }

    //labelHeat = labelSTD;

    // Set the map bounds.
    mesh->heatMapBounds() = mesh->calcHeatMapBounds();
    // Unit of the heat map
    mesh->heatMapUnit() = QString("#");
    // Tell the system the triangles have changed (here, the heat map)
    mesh->updateTriangles();

    return true;
  }
  REGISTER_PROCESS(HeatMapAreaAsymmetry);





  // Copy parent labels overtop of labels
  bool CopyParentsToLabels::run(Mesh* mesh)
  {
    vvGraph& S = mesh->graph();
    IntIntAttr& parents = mesh->parents();
    forall(const vertex& v, S)
      if(v->label > 0) {
        if(parents[v->label] > 0)
          v->label = parents[v->label];
        else
          v->label = 0;
      }

    parents.clear();

    mesh->updateAll();
    return true;
  }
  REGISTER_PROCESS(CopyParentsToLabels);

  // Copy labels to parents
  bool CopyLabelsToParents::run(Mesh* mesh)
  {
    vvGraph& S = mesh->graph();
    IntIntAttr& parents = mesh->parents();
    parents.clear();
    forall(const vertex& v, S)
      if(v->label > 0)
        parents[v->label] = v->label;

    mesh->updateAll();
    return true;
  }
  REGISTER_PROCESS(CopyLabelsToParents);



 struct CompareNeighSize
 {
    bool operator()(const std::pair<int,std::set<int> > & left, const std::pair<int,std::set<int> > & right){
      return left.second.size() < right.second.size();
    }

 };


 bool CorrectLabeling::run(Mesh* mesh, QString numColorString, QString colormapSizeString, QString balanceDistributionString, bool mesh3D)
  {
    int num_color = numColorString.toInt();// 16; //PASS IN PARAMETER;
    int colormap_size = colormapSizeString.toInt();
    bool balance_distribution = stringToBool(balanceDistributionString);
    std::cout<<num_color<<"  "<<colormap_size<<" "<<balance_distribution<<std::endl;
    if(num_color>colormap_size){
      std::cout<<"Number of colors is greater than color map size, decreased to match"<<std::endl;
      num_color = colormap_size;
    }
    if(num_color<8){
      std::cout<<"CorrectLabeling: number of colors is too small, increased to 7"<<std::endl;
      num_color=8;
    }

    vvGraph& S = mesh->graph();
    std::map<IntIntPair, double> neighPair;
    IntIntSetMap neighMap;
    IntIntSetMap neighColor;
    IntIntMap reLabel;

    if(mesh3D){
      NhbdGraphInfo info;
      neighborhoodGraph(S, 0.001, info);
      neighPair=info.sharedWallArea;
    } else {
      neighborhood2D(S,neighPair);
    }

    //Store neighborhoods
    std::map<IntIntPair, double>::iterator it = neighPair.begin();
    while(it!= neighPair.end()){
      IntIntPair cur_pair = it->first;
      neighMap[cur_pair.first].insert(cur_pair.second);
      it++;
    }


    //neighColor = neighMap;
    //std::map<int, std::vector<int> >::iterator it = neighColor.begin();
    //while(it!= neighColor.end()){
    //  for(unsigned int i=0;i<it->second.size();i++)
    //    it->second[i] = -1;
    //}

    std::vector<int> color_histo;
    color_histo.resize(num_color);
    for(int i=0;i<num_color;i++)
      color_histo[i]=0;

    int max_color = -1;
    while(neighMap.size()){
      std::pair<int, std::set<int> > min_deg = *min_element(neighMap.begin(),neighMap.end(),CompareNeighSize());
      int cur_color = -1;
      //Find minimum available color
      if(!balance_distribution){
        for(int i=0;i<num_color;i++)
            if(!neighColor[min_deg.first].count(i)){
              cur_color = i;
              break;
            }
      }
      else{
            int cur_freq = 1e5;
          for(int i=0;i<num_color;i++){
              if(!neighColor[min_deg.first].count(i) && color_histo[i]<cur_freq){
                cur_color = i;
                cur_freq = color_histo[i];

              }
               //std::cout<<cur_freq<<":"<<color_histo[i]<<std::endl;
          }
      }
      if(cur_color == -1){
        setErrorMessage("Number of colors is too small, some adjacent cells may have the same color. Rerun with a larger number of colors.");
        cur_color=0;
      }
      reLabel[min_deg.first] = cur_color+1+color_histo[cur_color]*colormap_size;
      color_histo[cur_color]++;
      if(cur_color>max_color)
        max_color = cur_color;

      //Erase from neighbors & add color to neighbors
      std::set<int>::iterator it_neigh;
      for(it_neigh = min_deg.second.begin();it_neigh != min_deg.second.end();it_neigh++){
        int cur_index = *it_neigh;
        neighColor[cur_index].insert(cur_color);
        neighMap[cur_index].erase(min_deg.first);
      }

      neighMap.erase(min_deg.first);
    }


      //Relabel based on color and color-map size



//    forall(const IntIntPair& p, parentMap)


    IntIntAttr& parents = mesh->parents();
    parents.clear();
    forall(const vertex& v, S)
      if(v->label > 0)
        parents[v->label] = reLabel[v->label];

    mesh->updateAll();
    std::cout<<"Relabeled with the folowing number of colors: "<<max_color+1<<std::endl;

    return true;
  }
  REGISTER_PROCESS(CorrectLabeling);

  bool CorrectLabelingStack::run(Store* store, QString numColorString, QString colormapSizeString, QString balanceDistributionString)
    {
      int num_color = numColorString.toInt();// 16; //PASS IN PARAMETER;
      int colormap_size = colormapSizeString.toInt();
      bool balance_distribution = stringToBool(balanceDistributionString);
      std::cout<<num_color<<"  "<<colormap_size<<" "<<balance_distribution<<std::endl;
      if(num_color>colormap_size){
        std::cout<<"Number of colors is greater than color map size, decreased to match"<<std::endl;
        num_color = colormap_size;
      }
      if(num_color<8){
        std::cout<<"CorrectLabeling: number of colors is too small, increased to 7"<<std::endl;
        num_color=8;
      }

      //vvGraph& S = mesh->graph();
      std::map<IntIntPair, double> neighPair;
      std::map<int, std::set<int> > neighMap;
      std::map<int, std::set<int> > neighColor;
      std::map<int, int> reLabel;

      HVecUS& dataV = store->data();
      const Stack* stack = store->stack();
      Point3i imgSize(stack->size());

      int xIdx = 0;
      #pragma omp parallel for schedule(guided)
      for(int z = 0; z < imgSize.z(); ++z)
        for(int y = 0; y < imgSize.y(); ++y)
          for(int x = 0; x < imgSize.x(); ++x) {
            Point3i p(x,y,z);
            int label = dataV[stack->offset(p)];
            if(label == 0) continue;
            std::set<int> n = findNeighborsLabel(store, imgSize, p);
            forall(int l, n){

              if(l != label and l != 0){
                #pragma omp critical
                {
                  neighMap[label].insert(l);
                }
              }
            }

          }

      // forall(auto p, neighMap){
      //   std::cout << "l  " << p.first << " /// ";
      //   forall(auto n, p.second){
      //     std::cout << n << " / ";
      //   }
      //   std::cout << std::endl;
      // }

      std::vector<int> color_histo;
      color_histo.resize(num_color);
      for(int i=0;i<num_color;i++)
        color_histo[i]=0;

      int max_color = -1;
      while(neighMap.size()){
        std::pair<int, std::set<int> > min_deg = *min_element(neighMap.begin(),neighMap.end(),CompareNeighSize());
        int cur_color = -1;
        //Find minimum available color
        if(!balance_distribution){
          for(int i=0;i<num_color;i++)
              if(!neighColor[min_deg.first].count(i)){
                cur_color = i;
                break;
              }
        }
        else{
              int cur_freq = 1e5;
            for(int i=0;i<num_color;i++){
                if(!neighColor[min_deg.first].count(i) && color_histo[i]<cur_freq){
                  cur_color = i;
                  cur_freq = color_histo[i];

                }
                 //std::cout<<cur_freq<<":"<<color_histo[i]<<std::endl;
            }
        }
        if(cur_color == -1){
          setErrorMessage("Number of colors is too small, some adjacent cells may have the same color. Rerun with a larger number of colors.");
          cur_color=0;
        }
        reLabel[min_deg.first] = cur_color+1+color_histo[cur_color]*colormap_size;
        color_histo[cur_color]++;
        if(cur_color>max_color)
          max_color = cur_color;

        //Erase from neighbors & add color to neighbors
        std::set<int>::iterator it_neigh;
        for(it_neigh = min_deg.second.begin();it_neigh != min_deg.second.end();it_neigh++){
          int cur_index = *it_neigh;
          neighColor[cur_index].insert(cur_color);
          neighMap[cur_index].erase(min_deg.first);
        }

        neighMap.erase(min_deg.first);
      }


      xIdx = 0;
      for(int z = 0; z < imgSize.z(); ++z)
        for(int y = 0; y < imgSize.y(); ++y)
          for(int x = 0; x < imgSize.x(); ++x, ++xIdx) {
            Point3i p(x,y,z);
            dataV[stack->offset(p)] = reLabel[dataV[stack->offset(p)]];
          }


      std::cout<<"Relabeled with the folowing number of colors: "<<max_color+1<<std::endl;
      store->changed();

      return true;
    }
    REGISTER_PROCESS(CorrectLabelingStack);

  bool SetParent::run(Mesh *mesh, int parent)
  {
    const std::vector<vertex> &vs = mesh->activeVertices();
    for(size_t i = 0; i < vs.size(); ++i) {
      vertex v = vs[i];
      if(v->selected and v->label > 0)
        mesh->parents()[v->label] = parent;
    }
    mesh->updateTriangles();
    return true;
  }
  REGISTER_PROCESS(SetParent);

  bool readParentFile(QString fileName, std::unordered_map<int,int>& labelParentMap)
  {
    labelParentMap.clear();

    QFile file(fileName);
    if(!file.open(QIODevice::ReadOnly))
      throw(QString("File '%1' cannot be opened for reading").arg(fileName));

    QTextStream ss(&file);
    QString line = ss.readLine();


    QStringList fields = line.split(",");
    if(fields.size() != 2)
      throw(QString("File '%1' should be in the format 'label, parent'").arg(fileName));

    int lineNum = 1;
    while(ss.status() == QTextStream::Ok) {
      ++lineNum;

      // Get the line and split it
      fields = ss.readLine().split(",");
      if(fields.size() != 2)
        break;

      // Get the data
      bool ok;
      int label = fields[0].toInt(&ok);
      if(!ok)
        throw(QString("Error line %1: the first column is not an integer number but '%2'").arg(fileName).arg(lineNum));

      int parent = fields[1].toInt(&ok);
      if(!ok)
        throw(QString("Error line %1: the second column is not an integer number but '%2'").arg(fileName).arg(lineNum));

      labelParentMap[label] = parent;
    }


    return true;
  }

  bool saveParentFile(QString filename, std::unordered_map<int, int>& parentMap)
  {
    // Open output file
    QFile file(filename);
    if(!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
      throw(QString("File '%1' cannot be opened for writing").arg(filename));
      return false;
    }
    QTextStream out(&file);
    // Write header
    out << "Label,Parent Label" << endl;

    // Write data
    forall(const IntIntPair& p, parentMap) {
      int label = p.first;
      int parent = p.second;
      out << label << "," << parent << endl;
    }
    file.close();

    return true;
  }

  bool MergeParentFiles::initialize(QWidget* parent)
  {
    QString suffix = ".csv";

    QString fileName1(parm("File Open T1T0"));
    if(parent)
      fileName1 = QFileDialog::getOpenFileName(parent, "Choose parent label file to load", parm("File Open T1T0"),
           "CSV files (*.csv)");

    // check file
    if(fileName1.isEmpty())
      throw(QString("File name is empty"));

    // check ending, add suffix if required
    if(fileName1.right(suffix.size()) != suffix)
      fileName1.append(suffix);

    setParm("File Open T1T0", fileName1);

    QString fileName2(parm("File Open T2T1"));
    if(parent)
      fileName2 = QFileDialog::getOpenFileName(parent, "Choose parent label file to load", parm("File Open T2T1"),
           "CSV files (*.csv)");

    // check file
    if(fileName2.isEmpty())
      throw(QString("File name is empty"));

    // check ending, add suffix if required
    if(fileName2.right(suffix.size()) != suffix)
      fileName2.append(suffix);

    setParm("File Open T2T1", fileName2);


    QString filenameSave(parm("File Save T2T0"));
    if(parent)
      filenameSave = QFileDialog::getSaveFileName(parent, "Select file to save labels in", parm("File Save T2T0"), "CSV files (*.csv)");
    // check filename
    if(filenameSave.isEmpty())
      return false;
    // check ending, add suffix if required
    if(filenameSave.right(suffix.size()) != suffix)
      filenameSave.append(suffix);

    setParm("File Save T2T0", filenameSave);

    return true;
  }

  bool MergeParentFiles::run(QString fileName1, QString fileName2, QString fileNameSave)
  {
    std::unordered_map<int, int> labelParentT1T0, labelParentT2T1, labelParentT2T0;
    readParentFile(fileName1, labelParentT1T0);
    readParentFile(fileName2, labelParentT2T1);

    forall(IntIntPair p, labelParentT2T1){
      if(labelParentT1T0.find(p.second) == labelParentT1T0.end()) continue;
      labelParentT2T0[p.first] = labelParentT1T0[p.second];
    }

    saveParentFile(fileNameSave, labelParentT2T0);

    return true;
  }
  REGISTER_PROCESS(MergeParentFiles);


  bool CreateParentAttr::run(Mesh* m, QString prefix, QString name)
  {

    AttrMap<int, int>& attrData = m->attributes().attrMap<int, int>(prefix + " " + name);
    attrData.clear();

    forall(auto p, m->parents()){
      attrData[p.first] = p.second;
    }

    return true;
  }
  REGISTER_PROCESS(CreateParentAttr);

  bool UniqueParentsFromAttr::run(Mesh* m, QString prefix, QString name1, QString name2, bool mult10)
  {

    AttrMap<int, int>& attrData1 = m->attributes().attrMap<int, int>(prefix + " " + name1);
    if(attrData1.empty()) return setErrorMessage("Attr Map 1 doesn't exist.");

    AttrMap<int, int>& attrData2 = m->attributes().attrMap<int, int>(prefix + " " + name2);
    if(attrData2.empty()) return setErrorMessage("Attr Map 2 doesn't exist.");

    int maxP1 = 0;
    int maxP2 = 0;

    IntSet setP1, setP2;
    IntVec vecP1, vecP2;
    IntIntMap valueKeyP1, valueKeyP2;

    forall(auto p, attrData1){
      if(p.second > maxP1) maxP1 = p.second;
      setP1.insert(p.second);
    }
    forall(auto p, attrData2){
      if(p.second > maxP2) maxP2 = p.second;
      setP2.insert(p.second);
    }

    forall(int l, setP1){
      vecP1.push_back(l);
      valueKeyP1[l] = vecP1.size();
    }

    forall(int l, setP2){
      vecP2.push_back(l);
      valueKeyP2[l] = vecP2.size();
    }

    std::set<int> allCells = findAllLabelsSet(m->graph());

    forall(int l, allCells){
      int p1 = attrData1[l];
      int p2 = attrData2[l];
      std::cout << "cell " << l << "/" << p1 << "/" << p2 << "/" << valueKeyP1[p1] << "/" << valueKeyP2[p2] << "/./" << vecP1.size() << "/./" << vecP2.size() << "/" << valueKeyP1[p1] + (vecP1.size()+1) * valueKeyP2[p2] << std::endl;
      m->parents()[l] = valueKeyP1[p1] + (vecP1.size()+1) * valueKeyP2[p2];
      if(mult10) m->parents()[l] = valueKeyP1[p1] + pow(10,((int)(log10(vecP1.size())+1))) * valueKeyP2[p2];
      if(p1 == 0 and p2 == 0) m->parents()[l] = 0;
    }

    m->updateTriangles();

    return true;
  }
  REGISTER_PROCESS(UniqueParentsFromAttr);



  bool ImportParentAttr::run(Mesh* m, Mesh* m2, QString prefix, QString name, QString importMesh)
  {

    AttrMap<int, int>& attrData = m->attributes().attrMap<int, int>(prefix + " " + name);

    if(attrData.empty()){
      return setErrorMessage("Attribute Map empty!");
    }

    if(importMesh == "Active Mesh"){
      m->parents().clear();
      forall(auto p, attrData){
        m->parents()[p.first] = p.second;
      }
      m->setUseParents(true);
      m->updateTriangles();
    } else if(importMesh == "Other Mesh (T2)"){
      AttrMap<int,int> newParents;
      forall(auto p, m2->parents()){
        newParents[p.first] = m->parents()[p.second];
      }
      m2->parents() = newParents;
      m2->setUseParents(true);
      m2->updateTriangles();
    } else {
      m2->parents().clear();
      forall(auto p, attrData){
        m2->parents()[m->parents()[p.first]] = p.second;
      }
      m2->setUseParents(true);
      m2->updateTriangles();
    }


    

    return true;
  }
  REGISTER_PROCESS(ImportParentAttr);


  // fill a QTreeWidget with all existing measures
  void fillTreeWidgetWithParentAttrs(Mesh* m, QTreeWidget* tree)
  {
    tree->clear();

    Attributes *attributes;
    attributes = &m->attributes();
    QStringList attrList = attributes->getAttrList();
    QStringList measureAttr;

    attrList.sort(Qt::CaseSensitive);

    for(const QString &text : attrList) {
      QStringList list = text.split(" ");

      if(list.size() < 4) 
        continue;
      if(list[0] != "Measure") 
        continue;
      if(list[1] != "Label") 
        continue;
      if(list[2] != "Int") 
        continue;
      QString name, attrName;
      for(int i = 1; i < list.size(); i++){
        if(i == 1) 
          name = list[i];
        else
          name += " " + list[i];
        if(i == 3)
          attrName = list[i];
        else if(i > 3)
          attrName += " " + list[i];
      }

      QTreeWidgetItem *item = new QTreeWidgetItem(QStringList() << attrName);
      item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable);
      tree->addTopLevelItem(item);
    }

}


    //Update heat map for the selected map
    void ImportParentAttrNew::on_attrTree_clicked(QTreeWidgetItem *item, int column){
        qDebug()<<" mapTree ";
        if(ui.attrMapTree->currentItem() == NULL) return; // in case there is no item selected but just the box ticked
        Mesh *m = mesh(0);
        currentM = m;
        qDebug()<<ui.attrMapTree->currentItem()->text(0);
        QString name = ui.attrMapTree->currentItem()->text(0);
        name = name.trimmed();
        QString attrName = "Measure Label Int " + name;
        currentItem = name;

        QString parentAttrName = "Measure Parent ID " + name;

        AttrMap<int, int>& attrData = m->attributes().attrMap<int, int>(attrName);
        AttrMap<int, QString>& parentAttrData = m->attributes().attrMap<int, QString>(parentAttrName);
        
        m->parents().clear();
        std::set<int> parentValues;
        forall(auto p, attrData){
          m->parents()[p.first] = p.second;
          parentValues.insert(p.second);
        }
        m->setUseParents(true);
        m->updateTriangles();

        QTreeWidget* valueTree = ui.valueTree;
        valueTree->clear();

        forall(int p, parentValues){
          QString pID = "";
          if(parentAttrData.find(p) != parentAttrData.end()){
            pID = parentAttrData[p];
          }
          QTreeWidgetItem *item = new QTreeWidgetItem(QStringList() << QString::number(p) << pID);
          item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable | Qt::ItemIsEditable);
          valueTree->addTopLevelItem(item);
        }

        updateState();
        updateViewer();
        return;
    }

    //Update heat map for the selected map
    void ImportParentAttrNew::on_attrTree2_clicked(QTreeWidgetItem *item, int column){
        qDebug()<<" mapTree ";
        if(ui.attrMapTree2->currentItem() == NULL) return; // in case there is no item selected but just the box ticked
        Mesh *m = mesh(1);
        currentM = m;
        qDebug()<<ui.attrMapTree2->currentItem()->text(0);
        QString name = ui.attrMapTree2->currentItem()->text(0);
        name = name.trimmed();
        QString attrName = "Measure Label Int " + name;
        currentItem = name;

        QString parentAttrName = "Measure Parent ID " + name;

        AttrMap<int, int>& attrData = m->attributes().attrMap<int, int>(attrName);
        AttrMap<int, QString>& parentAttrData = m->attributes().attrMap<int, QString>(parentAttrName);
        
        m->parents().clear();
        std::set<int> parentValues;
        forall(auto p, attrData){
          m->parents()[p.first] = p.second;
          parentValues.insert(p.second);
        }
        m->setUseParents(true);
        m->updateTriangles();

        QTreeWidget* valueTree = ui.valueTree;
        valueTree->clear();

        forall(int p, parentValues){
          QString pID = "";
          if(parentAttrData.find(p) != parentAttrData.end()){
            pID = parentAttrData[p];
          }
          QTreeWidgetItem *item = new QTreeWidgetItem(QStringList() << QString::number(p) << pID);
          item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable | Qt::ItemIsEditable);
          valueTree->addTopLevelItem(item);
        }

        updateState();
        updateViewer();
        return;
    }

    //Update heat map for the selected map
    void ImportParentAttrNew::on_deleteButton_clicked(){
        qDebug()<<" mapTree ";
        if(currentItem == "")
          return;

        Mesh *m = mesh(0);

        m->attributes().erase("Measure Label Int " + currentItem);

        fillTreeWidgetWithParentAttrs(m,ui.attrMapTree);
        return;
    }
    //Update heat map for the selected map
    void ImportParentAttrNew::on_deleteButton2_clicked(){
        qDebug()<<" mapTree ";
        if(currentItem == "")
          return;

        Mesh *m = mesh(1);

        m->attributes().erase("Measure Label Int " + currentItem);

        fillTreeWidgetWithParentAttrs(m,ui.attrMapTree2);
        return;
    }

    //Update heat map for the selected map
    void ImportParentAttrNew::on_copy12_clicked(){
        qDebug()<<" mapTree ";
        if(currentItem == "")
          return;

        Mesh *m = mesh(0);
        Mesh *m2 = mesh(1);

        QString name1 = ui.attrMapTree->currentItem()->text(0);
        QString name2 = ui.attrMapTree2->currentItem()->text(0);

        name1 = name1.trimmed();
        QString attrName1 = "Measure Label Int " + name1;
        AttrMap<int, int>& attrData1 = m->attributes().attrMap<int, int>(attrName1);

        name2 = name2.trimmed();
        QString attrName2 = "Measure Label Int " + name2;
        AttrMap<int, int>& attrData2 = m2->attributes().attrMap<int, int>(attrName2);

        AttrMap<int, int>& attrData12 = m2->attributes().attrMap<int, int>(attrName1);
        attrData12.clear();

        forall(auto p, attrData2){
          attrData12[p.first] = attrData1[p.second];
        }

        fillTreeWidgetWithParentAttrs(m2,ui.attrMapTree2);

        return;
    }

    void ImportParentAttrNew::on_saveParentIDs_clicked(){

      QStringList newList;
      Mesh* m = currentM;

      QString attrName = "Measure Label Int " + currentItem;
      QString parentAttrName = "Measure Parent ID " + currentItem;

      AttrMap<int, int>& attrData = m->attributes().attrMap<int, int>(attrName);
      AttrMap<int, QString>& parentAttrData = m->attributes().attrMap<int, QString>(parentAttrName);

      std::set<int> parentValues;
      forall(auto p, attrData){
        parentValues.insert(p.second);
      }
      
      for(int itemCount = 0; itemCount < ui.valueTree->topLevelItemCount(); ++itemCount){
        QString value = ui.valueTree->topLevelItem(itemCount)->text(0);
        QString id = ui.valueTree->topLevelItem(itemCount)->text(1);
        qDebug() << "debug: " << value << "/" << id << "\n";

        int parentL = value.toInt();

        qDebug() << "debug: " << value << "/" << id << "/" << parentL << "\n";

        if(parentValues.find(parentL) != parentValues.end()){
          parentAttrData[parentL] = id;
          qDebug() << "written\n";
        }
      }

      return;
    }

  bool ImportParentAttrNew::initialize(QWidget *parent)
  {
    Mesh *m = mesh(0);
    Mesh *m2 = mesh(1);

    AttrMap<int, int>& m1_current = m->attributes().attrMap<int, int>("Measure Label Int CurrentParents");
    forall(auto p, m->parents()){
      m1_current[p.first] = p.second;
    }
    AttrMap<int, int>& m2_current = m2->attributes().attrMap<int, int>("Measure Label Int CurrentParents");
    forall(auto p, m2->parents()){
      m2_current[p.first] = p.second;
    }

    // attributes = &m->attributes();
    QDialog dlg(parent);
    ui.setupUi(&dlg);

    // QStringList attr = attributes->getAttrList();
    fillTreeWidgetWithParentAttrs(m,ui.attrMapTree);
    fillTreeWidgetWithParentAttrs(m2,ui.attrMapTree2);
    connect(ui.attrMapTree, SIGNAL(itemClicked(QTreeWidgetItem *, int)),
            this, SLOT(on_attrTree_clicked(QTreeWidgetItem *, int )));
    connect(ui.attrMapTree2, SIGNAL(itemClicked(QTreeWidgetItem *, int)),
            this, SLOT(on_attrTree2_clicked(QTreeWidgetItem *, int )));
    connect(ui.deleteButton, SIGNAL(clicked()), this, SLOT(on_deleteButton_clicked()));
    connect(ui.copy12, SIGNAL(clicked()), this, SLOT(on_copy12_clicked()));
    connect(ui.saveParentIDs, SIGNAL(clicked()), this, SLOT(on_saveParentIDs_clicked()));
    // connect(ui.savePushButton, SIGNAL(clicked()), this, SLOT(on_savePushButton_clicked()));
    dlg.exec();

    return true;
  }

  bool ImportParentAttrNew::run(Mesh* m, Mesh* m2, QString prefix, QString name, QString importMesh)
  { 

    m->attributes().erase("Measure Label Int CurrentParents");
    m2->attributes().erase("Measure Label Int CurrentParents");
    return true;
  }
  REGISTER_PROCESS(ImportParentAttrNew);


  bool SelectParentLabel::run(Mesh *m, int labelToSelect, bool keepSelection)
  {

    forall(const vertex& v, m->graph()){
      if(!keepSelection) v->selected = false;
      if(m->parents()[v->label] == labelToSelect){
        v->selected = true;
      }
    }

    m->correctSelection(true);
    m->updateSelection();

    return true;

  }
  REGISTER_PROCESS(SelectParentLabel);

  bool SelectParentLabelsT1::run(Mesh *m, Mesh *m2)
  {

    std::set<int> parentLabelsT2;

    forall(auto p, m2->parents()){
      parentLabelsT2.insert(p.second);
    }

    vvGraph& S = m->graph();

    forall(const vertex& v, S){
      v->selected = false;
      if(v->label < 1) continue;
      if(parentLabelsT2.find(v->label)==parentLabelsT2.end()) continue;
      v->selected = true;
    }

    forall(const vertex& v, S){
      if(v->selected) continue;
      if(v->label > -1) continue;
      forall(const vertex& n, S.neighbors(v)){
        if(n->selected) v->selected = true;
      }
    }


    m->updateSelection();

    return true;

  }
  REGISTER_PROCESS(SelectParentLabelsT1);

  bool NewWalls::run(Mesh* mesh1, Mesh* mesh2)
  {
    vvGraph S1 = mesh1->graph();
    vvGraph S2 = mesh2->graph();

    if(!mesh2->showSurface()){
      setErrorMessage(QString("First turn on mesh visibility on the second timepoint."));
      return false;
    }

    // Check if all vertices labeled (see make cell mesh)

    /// make a set of all the labels in S1 and in S2.
    IntSet labelsS1;
    IntSet labelsS2;
    forall(const vertex& v, S1)
      if(v->label > 0)
        labelsS1.insert(v->label);
    forall(const vertex& v, S2)
      if(v->label > 0)
        labelsS2.insert(v->label);

    // Check the parentLabels map for cells which are not in the mesh1 or mesh2
    IntIntAttr& daughterParentMap = mesh2->parents();
    if(daughterParentMap.size() != 0) 
      forall(IntIntPair it, daughterParentMap){ 
        int lParent = it.second;
        int lDaughter = it.first;
        if(labelsS1.count(lParent) == 0 or labelsS2.count(lDaughter) == 0){
          setErrorMessage(QString("Problems with parent labels. Run Correct Parents first."));
          return false;
        }
      }
    else {
      setErrorMessage(QString("Load parent labels first."));
      return false;
    }

    // Look at the daughters (labels in S2) for each label in S1
    // parentDaughtersMap: key = parent in S1, value = labels of daughters in S2
    IntIntSetMap parentDaughtersMap;
    forall(int labelDaughter, labelsS2) {
      if(labelDaughter > 0 ) { 
        int labelParent = 0; 
        // add to daughterParentMap 0 values for the cells that do not have parent in T1.
        if (daughterParentMap.count(labelDaughter) == 0)  
          daughterParentMap[labelDaughter] = 0;
        labelParent = daughterParentMap.find(labelDaughter)->second;
        IntSet LDaughters;
        if (parentDaughtersMap.count(labelParent) > 0)
          LDaughters = parentDaughtersMap.find(labelParent)->second;
        LDaughters.insert(labelDaughter);
        parentDaughtersMap[labelParent] = LDaughters;
      }
    }

//{
//  cout << endl;
//  cout << "///////////parentDaughtersMap: " << endl;  
//  IntLabelsMap::iterator it;
//  for (it = parentDaughtersMap.begin(); it != parentDaughtersMap.end(); ++it)
//  {
//    cout << "labelParent: " << it->first << ", labelDaughters: " ; 
//    set<int> LDaughters = it->second;
//    forall(int labelD, LDaughters)
//      cout << labelD << ", " ; 
//    cout << endl;
//  }
//}

    // Map walls and border vertices 
    // key =  pair of labels to identify each wall, value = set of vertices belonging to wall
    std::map<std::pair<int,int>, std::set<vertex> > wallsS2; 
    forall(vertex v, S2) {
      IntSet labels;
      if(v->label == -1){ 
        // list the labels at cell border
        IntSet labelsNeighbors;
        forall(vertex n, S2.neighbors(v)){
          if(n->label > 0){
            labelsNeighbors.insert(n->label);
          }
        }
        if(labelsNeighbors.size()>3)
          continue; 
        for(IntSet::iterator l1 = labelsNeighbors.begin(); l1 != labelsNeighbors.end(); ++l1){
          IntIntPair pairLabels;
          for(IntSet::iterator l2 = labelsNeighbors.begin(); l2 != labelsNeighbors.end(); ++l2){
            if(*l1 >= *l2)
              continue; 
            pairLabels = std::make_pair(*l1, *l2);
            std::set<vertex> verticesBorder;
            if(wallsS2.count(pairLabels) != 0)
              verticesBorder = wallsS2.find(pairLabels)->second;
            verticesBorder.insert(v);
            wallsS2[pairLabels] = verticesBorder;
          }
        }
      }
    }

//{
//  cout << endl;
//  cout << "///////////wallsS2: " << endl;  
//  std::map<pair<int,int>, set<vertex> >::iterator it;
//  for (it = wallsS2.begin(); it != wallsS2.end(); ++it)
//  {
//     pair<int,int> walls = it->first;
//    cout << "wallsS2: " << walls.first << ", " << walls.second << ". nVertices: " << it->second.size() << endl;
//
//  }
//}

    // Compare the labels of daughter cells with the labels of walls in S2. 
    std::map<int, std::set<IntIntPair> > newWalls; 
    for(IntIntSetMap::iterator it = parentDaughtersMap.begin(); it != parentDaughtersMap.end(); ++it) {
      int labelParent= it->first;
      if(labelParent == 0)
        continue; 
      IntSet LDaughters = it->second;
      if(LDaughters.count(0) == 1)
        continue;
      // look at all the pairs formed by daughter cells
      std::set<IntIntPair> allPairsLabels;
      for(IntSet::iterator LDaughter1 = LDaughters.begin(); LDaughter1 != LDaughters.end(); ++LDaughter1){
        for(IntSet::iterator LDaughter2 = LDaughters.begin(); LDaughter2 != LDaughters.end(); ++LDaughter2){
          if(*LDaughter1 >= *LDaughter2)
            continue; 
          //cout << "lParent" << labelParent << ", LDaughter1: " <<  *LDaughter1 << ", LDaughter2: " <<  *LDaughter2 << endl; 
          //cout << endl;
          IntIntPair pairLabels = std::make_pair(*LDaughter1, *LDaughter2);
          if(wallsS2.count(pairLabels) != 0)
            allPairsLabels.insert(pairLabels);
        }
      }
      newWalls[labelParent] = allPairsLabels;
    }

    // Select the vertices belonging to new walls 
    for(std::map<int, std::set<IntIntPair> >::iterator it = newWalls.begin(); it != newWalls.end(); ++it) {
      if(it->second.size() == 0)
        continue;
      auto newWallsPairs = it->second;
      forall(IntIntPair wall, newWallsPairs) {
        if(wallsS2.count(wall) != 0){
          std::set<vertex> wallVertices = wallsS2.find(wall)->second;
          //cout << "cell in S1: " << it->first <<  "nvertices: " << wallVertices.size() << endl; 
          forall(vertex v, wallVertices)
            v->selected = true;
        }  
      }
    }
      
  
    mesh2->updateAll();

//// print out the results (that's all we can do for now!)
//{
//  cout << endl;
//  cout << "///////////newWalls: " << endl;  
//   std::map<int, set<pair <int,int> > >::iterator it;
//  for (it = newWalls.begin(); it != newWalls.end(); ++it)
//    if(it->second.size() != 0)
//    {
//      cout << "labelParent: " << it->first << ", labelsnewwalls: " ; 
//      set<pair<int, int> > newWallsPairs = it->second;
//      for (set<pair<int, int> >::iterator p =  newWallsPairs.begin(); p != newWallsPairs.end(); ++p)
//        cout << p->first << ", " << p->second << endl; 
//    }
//  cout << endl;
//}

    return true;
  }
  REGISTER_PROCESS(NewWalls);

  REGISTER_PROCESS(SetCellType);
  REGISTER_PROCESS(ResetCellTypes);
  REGISTER_PROCESS(LoadCellTypes);
  REGISTER_PROCESS(SaveCellTypes);
  REGISTER_PROCESS(SelectCellType);

}
