//
// 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_LINEAGE_HPP
#define MESH_PROCESS_LINEAGE_HPP

#include <Process.hpp>
#include <SystemProcessLoad.hpp>
#include <QFileDialog>

#include <ui_ParentAttrDlg.h>

namespace mgx
{
  ///\addtogroup MeshProcess
  ///@{
  /**
   * \class CorrectParents ProcessLineage.hpp <MeshProcessLineage.hpp>
   *
   * Take out non-existing labels from parent map.
   */
  class mgxBase_EXPORT CorrectParents : public Process
  {
  public:
    CorrectParents(const Process& process) : Process(process)
    {
      setName("Mesh/Deformation/Restore Correct Parents");
      setDesc("Restores the correct parent labelling of the other mesh from the attribute map");
      setIcon(QIcon(":/images/ParentsCheck.png"));
	}

    bool run()
    {
      if(!checkState().mesh(MESH_NON_EMPTY, 0).mesh(MESH_NON_EMPTY, 1))
        return false;
      Mesh* mesh1, *mesh2;
      if(currentMesh() == mesh(0)) {
        mesh2 = mesh(0);
        mesh1 = mesh(1);
      } else if(currentMesh() == mesh(1)) {
        mesh2 = mesh(1);
        mesh1 = mesh(0);
      } else
        return false;

      bool res = run(mesh1, mesh2);
      return res;
    }

    bool run(Mesh* mesh1, Mesh* mesh2);

  };

  /**
   * \class HeatMapProliferation MeshProcessLineage.hpp <MeshProcessLineage.hpp>
   *
   * Compute the heap map that shows how many daughter cells a parent cell has.
   */
  class mgxBase_EXPORT HeatMapProliferation : public Process
  {
  public:
    HeatMapProliferation(const Process& process) : Process(process)
    {
      setName("Mesh/Lineage Tracking/Heat Map Proliferation");
      setDesc("Compute a heat map that shows the cell proliferation rate, (how may daughter cells a parent cell has).");
      setIcon(QIcon(":/images/HeatMapProliferation.png"));

      addParm("Use Parents On Other Mesh","Use Parents On Other Mesh", "No", booleanChoice()); // RSS FIXME shouldn't this be decided in the GUI? Soeren: Prolifeartion always needs parents, but it can only be computed on the later timepoint and it is difficult to transfer the heat map to the earlier time point, which this parameter facilitates
	  }

    bool run()
    {
      Mesh* m = currentMesh();
      if(!m)
        throw QString("%1:run No current mesh").arg(name());
      Mesh* m2 = otherMesh();
      if(!m2)
        throw QString("%1:run No other mesh").arg(name());

      bool res = run(m,m2, stringToBool(parm("Use Parents On Other Mesh")));
      if(res)
        m->setShowLabel("Label Heat");
      return res;
    }

    bool run(Mesh* m, Mesh* m2, bool onOtherMesh);
  };

 /**
   * \class MeshSelectNewWalls <MeshSelectNewWalls.hpp>
   */
  class mgxBase_EXPORT MeshSelectNewWalls : public Process
  {
  public:
    MeshSelectNewWalls(const Process &process) : Process(process) 
    {
      setName("Mesh/Lineage Tracking/Select New Walls");
      setDesc("Select Newly Divided Walls");
      setIcon(QIcon(":/images/NewWalls.png"));
    }

    bool run()
    {
      Mesh* m = currentMesh();
      if(!m)
        throw QString("%1:run No current mesh").arg(name());

      m->updateSelection();
      return run(m);
    }

    bool run(Mesh* m);
  };

  class mgxBase_EXPORT HeatMapAreaAsymmetry : public Process
  {
  public:
    HeatMapAreaAsymmetry(const Process& process) : Process(process)
    {
      setName("Mesh/Lineage Tracking/Daughter Area asymmetry");
      setDesc("Measures the asymmetry in cell-area in daughter cells. Uses standard-deviation of daughter area over mean daughter area (division by mean provides scale invariance)");
      setIcon(QIcon(":/images/HeatMapProliferation.png"));
	}

    bool run()
    {
      if(!checkState().mesh(MESH_USE_PARENTS)) {
        setErrorMessage("The current mesh must have show parents selected.");
        return false;
      }
      Mesh* m = currentMesh();

      bool res = run(m);
      if(res)
        m->setShowLabel("Label Heat");
      return res;
    }

    bool run(Mesh* m);

  };

  /**
   * \class CopyParentsToLabels ProcessLineage.hpp <MeshProcessLineage.hpp>
   *
   * Copy parents to labels, and clear parent table.
   */
  class mgxBase_EXPORT CopyParentsToLabels : public Process
  {
  public:
    CopyParentsToLabels(const Process& process) : Process(process)
    {
      setName("Mesh/Lineage Tracking/Copy Parents to Labels");
      setDesc("Copy parents to labels, and clear parent table.");
      setIcon(QIcon(":/images/ParentsCopyToLabel.png"));
	}

    bool run()
    {
      return run(currentMesh());
    }

    bool run(Mesh* m);

  };

  /**
   * \class CopyLabelsToParents ProcessLineage.hpp <MeshProcessLineage.hpp>
   *
   * Copy labels to parents
   */
  class mgxBase_EXPORT CopyLabelsToParents : public Process
  {
  public:
    CopyLabelsToParents(const Process& process) : Process(process)
    {
      setName("Mesh/Lineage Tracking/Copy Labels to Parents");
      setDesc("Copy labels to parents.");
      setIcon(QIcon(":/images/ParentsCopyToLabel.png"));
		}

    bool run()
    {
      Mesh* m = currentMesh();
      return run(m);
    }

    bool run(Mesh* m);

  };

  /**
   * \class CorrectLabeling <MeshProcessLineage.hpp>
   *
   * Creates a new labeling of the mesh that avoids common colors between neighbors
   */

  class mgxBase_EXPORT CorrectLabeling : public Process
  {
  public:
    CorrectLabeling(const Process& process) : Process(process)
    {
	  setName("Mesh/Lineage Tracking/Correct Labeling Colors");
	  setDesc("Relabels cells to avoid neighboring cells with the same color.");
      setIcon(QIcon(":/images/ParentsCopyToLabel.png"));

	  addParm("Number of colors","Number of colors used to color","7");
	  addParm("Colormap size","Size of the labels colormap","16");
	  addParm("Balance color distribution","Try to balance the number cells of each color","No",booleanChoice());
    addParm("3D Mesh","Set to Yes if the mesh has volumetric cells","No",booleanChoice());
		}

    bool run()
    {
      Mesh* m = currentMesh();
      return run(m,parm("Number of colors"),parm("Colormap size"),parm("Balance color distribution"), stringToBool(parm("3D Mesh")));
    }

    bool run(Mesh* mesh, QString numColorString, QString colormapSizeString, QString balanceDistributionString, bool mesh3D);

  };


    /**
     * \class CorrectLabeling <MeshProcessLineage.hpp>
     *
     * Creates a new labeling of the mesh that avoids common colors between neighbors
     */

    class mgxBase_EXPORT CorrectLabelingStack : public Process
    {
    public:
      CorrectLabelingStack(const Process& process) : Process(process)
      {
  	  setName("Stack/Segmentation/Correct Labeling Colors");
  	  setDesc("Relabels cells to avoid neighboring cells with the same color.");
        setIcon(QIcon(":/images/ParentsCopyToLabel.png"));

  	  addParm("Number of colors","Number of colors used to color","7");
  	  addParm("Colormap size","Size of the labels colormap","16");
  	  addParm("Balance color distribution","Try to balance the number cells of each color","No",booleanChoice());
  		}

      bool run()
      {
        Stack* stack = currentStack();
        Store* store = stack->currentStore();
        return run(store,parm("Number of colors"),parm("Colormap size"),parm("Balance color distribution"));
      }

      bool run(Store* store, QString numColorString, QString colormapSizeString, QString balanceDistributionString);

    };

  /**
   * Set parent
   *
   * \addtogroup MeshProcess
   */
  class mgxBase_EXPORT SetParent : public Process
  {
  public:
    SetParent(const Process& process) : Process(process)
    {
	  setName("Mesh/Lineage Tracking/Set Parent");
	  setDesc("Set the parent for selected cells");
      setIcon(QIcon(":/images/Parents.png"));

	  addParm("Label","Parent Number label","0");
	}

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

    bool run(Mesh *mesh, int parent);

  };

  /**
   * Process that resembles Aleksandra's Phython script to merge several Label-Parent files
   *
   * \addtogroup MeshProcess
   */
  class mgxBase_EXPORT MergeParentFiles : public Process
  {
  public:
    MergeParentFiles(const Process& process) : Process(process)
    {
	  setName("Mesh/Lineage Tracking/Merge Parent Files");
	  setDesc("Set the parent for selected cells");
      setIcon(QIcon(":/images/Parents.png"));

	  addParm("File Open T1T0","File Open T1T0","");
	  addParm("File Open T2T1","File Open T2T1","");
	  addParm("File Save T2T0","File Save T2T0","");
	}

    bool initialize(QWidget* parent);

    bool run()
    {
      return run(parm("File Open T1T0"), parm("File Open T2T1"), parm("File Save T2T0"));
    }

    bool run(QString file1, QString file2, QString fileNameSave);

  };

  class mgxBase_EXPORT CreateParentAttr : public Process
  {
  public:
    CreateParentAttr(const Process& process) : Process(process)
    {
	  setName("Mesh/Lineage Tracking/Parent Export to Attr Map");
	  setDesc("Creates an Attribute map of the chosen name (preceding Measure Label Int) using the current parent labels.");
      setIcon(QIcon(":/images/MakeHeatMap.png"));

	  addParm("Prefix","Prefix","Measure Label Int");
	  addParm("Attr Name","Attr Name","Parents");
		}
    //bool processParms();

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

    bool run(Mesh* m, QString prefix, QString name);

  };

  class mgxBase_EXPORT UniqueParentsFromAttr : public Process
  {
  public:
    UniqueParentsFromAttr(const Process& process) : Process(process)
    {
    setName("Mesh/Lineage Tracking/Unique Parents from Attrs");
    setDesc("Unique Parents from Attrs");
      setIcon(QIcon(":/images/MakeHeatMap.png"));

    addParm("Prefix","Prefix","Measure Label Int");
    addParm("Parent Attr 1","Parent Attr 1","P1");
    addParm("Parent Attr 2","Parent Attr 2","P2");
    addParm("Multiplier 10","Multiplier 10","No", booleanChoice());
    }

    bool run()
    {
      if(!checkState().mesh(MESH_ANY))
        return false;
      Mesh *m = currentMesh();
      return run(m, parm("Prefix"), parm("Parent Attr 1"), parm("Parent Attr 2"), stringToBool(parm("Multiplier 10")));
    }

    bool run(Mesh* m, QString prefix, QString name1, QString name2, bool mult10);

  };


  class mgxBase_EXPORT ImportParentAttr : public Process
  {
  public:
    ImportParentAttr(const Process& process) : Process(process)
    {
    setName("Mesh/Lineage Tracking/Parent Import from Attr Map");
    setDesc("Imports parent labels from an Attribute map of the chosen name (preceding Measure Label Int). Overwrites the current parents!");
      setIcon(QIcon(":/images/MakeHeatMap.png"));

    addParm("Prefix","Prefix","Measure Label Int");
    addParm("Attr Name","Attr Name","Parents");
    addParm("Import To Mesh","Import To Mesh","Active Mesh", QStringList() << "Active Mesh" << "Other Mesh (T1)"<< "Other Mesh (T2)");
    }
    bool processParms();

    bool run()
    {
      if(!checkState().mesh(MESH_ANY))
        return false;
      Mesh *m = currentMesh();
      Mesh *m2 = otherMesh();
      return run(m, m2, parm("Prefix"), parm("Attr Name"), parm("Import To Mesh"));
    }

    bool run(Mesh* m, Mesh* m2, QString prefix, QString name, QString importMesh);

  };

    class mgxBase_EXPORT ImportParentAttrNew : public Process
  {
    Q_OBJECT
    Ui_ParentAttrDlg ui;
  public:
    ImportParentAttrNew(const Process& process) : Process(process)
    {
    setName("Mesh/Lineage Tracking/Parent Import from Attr Map GUI");
    setDesc("Imports parent labels from an Attribute map of the chosen name (preceding Measure Label Int). Overwrites the current parents!");
      setIcon(QIcon(":/images/MakeHeatMap.png"));

    addParm("Prefix","Prefix","Measure Label Int");
    addParm("Attr Name","Attr Name","Parents");
    addParm("Import To Mesh","Import To Mesh","Active Mesh", QStringList() << "Active Mesh" << "Other Mesh (T1)"<< "Other Mesh (T2)");
    }
    bool initialize(QWidget *parent);
    bool processParms();

    bool run()
    {
      if(!checkState().mesh(MESH_ANY))
        return false;
      Mesh *m = mesh(0);
      Mesh *m2 = mesh(1);
      return run(m, m2, parm("Prefix"), parm("Attr Name"), parm("Import To Mesh"));
    }

    bool run(Mesh* m, Mesh* m2, QString prefix, QString name, QString importMesh);

    protected slots:
    
      void on_attrTree_clicked(QTreeWidgetItem *, int);
      void on_attrTree2_clicked(QTreeWidgetItem *, int);
      void on_deleteButton_clicked();
      void on_deleteButton2_clicked();
      void on_copy12_clicked();
      void on_saveParentIDs_clicked();

    protected:
      QDialog* dlg;
      QString currentItem;
      Mesh* currentM;
  };

  class mgxBase_EXPORT SelectParentLabel : public Process
  {
   public:
     SelectParentLabel(const Process& process) : Process(process)
     {
	   setName("Mesh/Lineage Tracking/Select Parents");
	   setDesc("Select Parents with a given label");
	   setIcon(QIcon(":/images/CellAtlas.png"));

	   addParm("Label","Label","0");
	   addParm("Keep Selection","Keep Selection","No",booleanChoice());
	 }

     bool run(){
       Mesh *m = currentMesh();
       return run(m, parm("Label").toInt(), stringToBool(parm("Keep Selection")));
     }
     bool run(Mesh *m, int labelToSelect, bool keepSelection);

  };

  class mgxBase_EXPORT SelectParentLabelsT1 : public Process
  {
   public:
     SelectParentLabelsT1(const Process& process) : Process(process)
     {
     setName("Mesh/Lineage Tracking/Select Parents in Parent Mesh");
     setDesc("Select cells in the earlier mesh that have a parents label in the later mesh.");
     setIcon(QIcon(":/images/CellAtlas.png"));

   }

     bool run(){
       Mesh *m = currentMesh();
       Mesh *m2 = otherMesh();
       return run(m, m2);
     }
     bool run(Mesh *m, Mesh *m2);

  };

  /**
   * Set cell type
   *
   * \addtogroup MeshProcess
   */
  class mgxBase_EXPORT SetCellType : public SetParent
  {
  public:
    SetCellType(const Process& process) : SetParent(process)
    {
    setName("Mesh/Cell Types/Set Cell Type");
    setDesc("Set the cell type for selected cells. Overwrite the parent label with the cell type number.");
    setIcon(QIcon(":/images/Parents.png"));

  }

  };

  /**
   * \class ResetParents MeshProcessLineage.hpp <MeshProcessLineage.hpp>
   *
   * Clear label to parent mapping
   *
   * \ingroup MeshProcess
   */
  class mgxBase_EXPORT ResetCellTypes : public ResetParents {
  public:
    ResetCellTypes(const Process& process) : ResetParents(process)
    {
      setName("Mesh/Cell Types/Reset Cell Types");
      setDesc("Clear mapping from parents to labels");
      setIcon(QIcon(":/images/ParentsClear.png"));
    }
  
  };

  /**
   * \class ResetParents MeshProcessLineage.hpp <MeshProcessLineage.hpp>
   *
   * Clear label to parent mapping
   *
   * \ingroup MeshProcess
   */
  class mgxBase_EXPORT LoadCellTypes : public LoadParents {
  public:
    LoadCellTypes(const Process& process) : LoadParents(process)
    {
      setName("Mesh/Cell Types/Load Cell Types");
      setDesc("Clear mapping from parents to labels");
    }
  
  };

  /**
   * \class ResetParents MeshProcessLineage.hpp <MeshProcessLineage.hpp>
   *
   * Clear label to parent mapping
   *
   * \ingroup MeshProcess
   */
  class mgxBase_EXPORT SaveCellTypes : public SaveParents {
  public:
    SaveCellTypes(const Process& process) : SaveParents(process)
    {
      setName("Mesh/Cell Types/Save Cell Types");
      setDesc("Clear mapping from parents to labels");
    }
  
  };


  class mgxBase_EXPORT SelectCellType : public Process
  {
   public:
     SelectCellType(const Process& process) : Process(process)
     {
       setName("Mesh/Cell Types/Select Cell Type");
       setDesc("Select Cell Types with a given label");
     }
  };

  class mgxBase_EXPORT NewWalls : public Process 
  {
  public:
    NewWalls(const Process& process) : Process(process) {}
  
    bool run(const QStringList& parms)
    {
      if(!checkState().mesh(MESH_NON_EMPTY, 0).mesh(MESH_NON_EMPTY, 1))
        return false;
      Mesh *mesh1, *mesh2;
      if(currentMesh() == mesh(0)) {
        mesh1 = mesh(0);
        mesh2 = mesh(1);
      }
      else if(currentMesh() == mesh(1)) {
        mesh1 = mesh(1);
        mesh2 = mesh(0);
      }
      else
        return false;

      if(!checkState().mesh(MESH_NON_EMPTY, 0).mesh(MESH_NON_EMPTY, 1))

        return false;
  
      return run(mesh1, mesh2);
    }

    bool run(Mesh *mesh1, Mesh *mesh2);
    
    QString name() const { return "Mesh/Lineage Tracking/New Walls"; }
    QString description() const { return "Select vertices belonging to new walls on second time point.\n"
      "First time point should be the active stack, parents should be loaded in second time point."; }
    QStringList parmNames() const { return QStringList(); }
    QStringList parmDescs() const { return QStringList(); }
    QStringList parmDefaults() const { return QStringList(); }
    QIcon icon() const { return QIcon(":/images/NewWalls.png"); }
  };

  ///@}
}

#endif
