//
// This file is part of MorphoGraphX - http://www.MorphoGraphX.org
// Copyright (C) 2012-2016 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_CELL_ANALYSIS_3D_HPP
#define MESH_PROCESS_CELL_ANALYSIS_3D_HPP

#include <Process.hpp>

namespace mgx
{


class mgxBase_EXPORT ExplodeMesh : public Process
  {
  public:
    ExplodeMesh(const Process& process) : Process(process)
    {
    setName("Mesh/Visualization 3D/Explode Mesh");
    setDesc("Creates an exploded view on the mesh");
      setIcon(QIcon(":/images/MakeHeatMap.png"));

    addParm("Factor X","Factor X","1");
    addParm("Factor Y","Factor Y","1");
    addParm("Factor Z","Factor Z","1");
    addParm("Only Selected","Only Selected","No",booleanChoice());
    addParm("Reverse Explosion","Reverse Explosion","Yes",booleanChoice());
    addParm("Parents Together","Parents Together","No",booleanChoice());
  }

    bool run()
    {
      Mesh* m = currentMesh();
      return run(m, parm("Factor X").toDouble(), parm("Factor Y").toDouble(), parm("Factor Z").toDouble(), stringToBool(parm("Only Selected")), stringToBool(parm("Reverse Explosion")), stringToBool(parm("Parents Together")));
    }

    bool run(Mesh* m, double factorX, double factorY, double factorZ, bool onlySelected, bool reverse, bool bundleParents);

  };

class mgxBase_EXPORT TranslateByParent : public Process
  {
  public:
    TranslateByParent(const Process& process) : Process(process)
    {
    setName("Mesh/Visualization 3D/Translate By Parent");
    setDesc("Creates an exploded view on the mesh");
      setIcon(QIcon(":/images/MakeHeatMap.png"));

    addParm("Transl X","Transl X","20");
    addParm("Transl Y","Transl Y","0");
    addParm("Transl Z","Transl Z","0");
    addParm("Only Selected","Only Selected","No",booleanChoice());
    addParm("Reverse Explosion","Reverse Explosion","Yes",booleanChoice());
    addParm("Consider Rotation","Consider Rotation","Yes",booleanChoice());
  }

    bool run()
    {
      Mesh* m = currentMesh();
      Matrix4d rotMatrix;
      m->stack()->getFrame().getMatrix(rotMatrix.data());
      // set translation to 0, we only care about the rotation
      rotMatrix[3][0] = 0;
      rotMatrix[3][1] = 0;
      rotMatrix[3][2] = 0;
      return run(m, parm("Transl X").toDouble(), parm("Transl Y").toDouble(), parm("Transl Z").toDouble(), stringToBool(parm("Only Selected")), stringToBool(parm("Reverse Explosion")), stringToBool(parm("Consider Rotation")), rotMatrix);
    }

    bool run(Mesh* m, double factorX, double factorY, double factorZ, bool onlySelected, bool reverse, bool rot, Matrix4d rotMatrix);

  };


class mgxBase_EXPORT RestoreOriginalMesh : public Process
  {
  public:
    RestoreOriginalMesh(const Process& process) : Process(process)
    {
      setName("Mesh/Visualization 3D/Restore Original Mesh");
      setDesc("Reverses any exloded view");
      setIcon(QIcon(":/images/MakeHeatMap.png"));
  }

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

    bool run(Mesh* m, bool vis);

  };

class mgxBase_EXPORT SetParentExploded : public Process
  {
  public:
    SetParentExploded(const Process& process) : Process(process)
    {
    setName("Mesh/Visualization 3D/Set Parent Exploded");
    setDesc("Sets a parent label and update its position depending on the explosion");
      setIcon(QIcon(":/images/MakeHeatMap.png"));

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

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

    bool run(Mesh* m, int label);

  };

 /**
   * \class LabelCellWalls MeshProcessCellAnalysis3D.hpp <MeshProcessCellAnalysis3D.hpp>
   *
   * Give each individual cell wall a separate label
   */
  class mgxBase_EXPORT LabelCellWalls : public Process
  {
  public:
    LabelCellWalls(const Process& process) : Process(process)
    {

      setName("Mesh/Cell Mesh/Tools/Label Cell Walls");
      setDesc("Give each individual cell wall a separate label.\n"
      "The Extend parameter extends walls that are not on the outside by x neighboring vertices.");
    setIcon(QIcon(":/images/Hex.png"));

      addParm("Extend","Extend Side Wall Labels into unlabelled regions","0");
      addParm("Only Attribute Map","If Yes, only the attribute maps are created, if No, the actual cell labels are changed","No",booleanChoice());
  }

    bool run()
    {
        Mesh *m = currentMesh();
        return run(m, parm("Extend").toInt(), stringToBool(parm("Only Attribute Map")));
    }

    bool run(Mesh *mesh1, int extend, bool onlyAttr);

  };

 /**
   * \class RestoreCellLabels MeshProcessCellAnalysis3D.hpp <MeshProcessCellAnalysis3D.hpp>
   *
   * Restore the original cell labels of 3D cells
   */
  class mgxBase_EXPORT RestoreCellLabels : public Process
  {
  public:
    RestoreCellLabels(const Process& process) : Process(process)
    {
      setName("Mesh/Cell Mesh/Tools/Restore Cell Labels");
      setDesc("Restore the original cell labels of 3D cells.");
      setIcon(QIcon(":/images/Hex.png"));
  }

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

  bool run(Mesh *mesh1);

  };

  /**
   * \class FindCellWallCenters MeshProcessCellAnalysis3D.hpp <MeshProcessCellAnalysis3D.hpp>
   *
   */
  class mgxBase_EXPORT FindCellWallCenters : public Process
  {
  public:
    FindCellWallCenters(const Process& process) : Process(process)
    {
      setName("Mesh/Cell Mesh/Tools/Cell Wall Centers");
      setDesc("Find the centers of the cell walls");
      setIcon(QIcon(":/images/Center.png"));
  
      addParm("Mode","TOREVISIT: Description missing","Average Tri Weighted", QStringList() << "Average Tri Weighted" << "Max Border Dis Vertex");
      addParm("Create Vtxs in Mesh2","TOREVISIT: Description missing","No",booleanChoice());
    }

    bool run()
    {
      Mesh *m = currentMesh();
      Mesh *m2 = mesh(1);
      if(m == mesh(1)) m2 = mesh(0);
      return run(m, m2, parm("Mode"), stringToBool(parm("Create Vtxs in Mesh2")));
    }
  
    bool run(Mesh *mesh1, Mesh *mesh2, QString mode, bool writeMesh);
  };

 /**
   * \class LabelCellWalls MeshProcessCellAnalysis3D.hpp <MeshProcessCellAnalysis3D.hpp>
   *
   * Give each individual cell wall a separate label
   */
  class mgxBase_EXPORT RotateCamera : public Process
  {
  public:
    RotateCamera(const Process& process) : Process(process)
    {
      setName("Mesh/Visualization 3D/Rotate Camera");
      setDesc("Rotates the camera to create simple anmations");
      setIcon(QIcon(":/images/Camera.png"));

      addParm("X Axis","Rotation angle around x axis","0.0");
      addParm("Y Axis","Rotation angle around y axis","0.0");
      addParm("Z Axis","Rotation angle around z axis","360.0");
      addParm("Steps","Rotation steps","360");
      addParm("Break (ms)","pause between steps","100.0");
      addParm("Stack 2","also rotate the other stack","No",booleanChoice());
    }

    bool run()
    {
        Mesh *m = currentMesh();
        Stack* s = currentStack();

        Mesh *m2 = otherMesh();
        Stack* s2 = otherStack();
        return run(s, m, s2, m2, parm("X Axis").toDouble(), parm("Y Axis").toDouble(), parm("Z Axis").toDouble(), parm("Steps").toInt(),
          parm("Break (ms)").toInt(), stringToBool(parm("Stack 2")));
     }

     bool run(Stack *s1, Mesh *m1, Stack *s2, Mesh *m2, double xRot, double yRot, double zRot, int steps, int breakStep, bool cameraStack2);

  };


  class mgxBase_EXPORT LabelNuclei : public Process
  {
  public:
    LabelNuclei(const Process& process) : Process(process)
    {
      setName("Mesh/Nucleus/Label Nuclei");
      setDesc("Label the nuclei in mesh 2 like the cells they are located in.");
      setIcon(QIcon(":/images/Nuclei.png"));
    }

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

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

  };

  class mgxBase_EXPORT DistanceNuclei : public Process
  {
  public:
    DistanceNuclei(const Process& process) : Process(process)
    {
      setName("Mesh/Nucleus/Distance Nuclei");
      setDesc("Compute distance from nucleus centroid to cell centroid. Cells in active mesh, nuclei in other mesh with cell parent labels.");
      setIcon(QIcon(":/images/Nuclei.png"));
    }

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

  class mgxBase_EXPORT SelectDuplicatedNuclei : public Process
  {
  public:
    SelectDuplicatedNuclei(const Process& process) : Process(process)
    {
      setName("Mesh/Nucleus/Select Duplicated Nuclei");
      setDesc("Select Duplicated Nuclei");
      setIcon(QIcon(":/images/Nuclei.png"));

      addParm("Distance Threshold","Distance Threshold","1.0");  // 0
    }

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

    bool run(Mesh* m, double disThreshold);
  };
}

#endif
