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

#include <Process.hpp>
#include <ProcessUtils.hpp>
#include <HejnowiczSurface.hpp>

namespace mgx
{
  /**
   * \class HejnowiczSurfaceParms HejnowiczSurface.hpp <HejnowiczSurface.hpp>
   *
   * Process to hold parameters Hejnowicz surface. In this class name() is not 
   * defined (and is virtual), so you will need to derive your own version 
   * of it to pass to HejnowiczSurfaceGrowth.
   *
   * \ingroup GrowingSurfaceProcesses
   */
  class mgxBase_EXPORT HejnowiczSurfaceParms : public Process 
  {
  public:
    HejnowiczSurfaceParms(const Process &process) : Process(process) {}

    /// Run is empty, this process is only for parameters
    bool run(const QStringList &) { return true; }

    QString description() const { return 
      "Parameters for Hejnowicz Surface (Hejnowicz and Karczewski 1993)"; }

    QStringList parmNames() const { return HejnowiczSurface::parmNames(); }
    QStringList parmDescs() const { return HejnowiczSurface::parmDescs(); }
    QStringList parmDefaults() const { return HejnowiczSurface::parmDefaults(); }

    ParmChoiceMap parmChoice() const
    {
      ParmChoiceMap map;
      map[HejnowiczSurface::pGrowthType] = QStringList() << "Hejnowicz" << "GrowthFunc" << "GrowthMap";
      return map;
    }

    // Icon file
    QIcon icon() const { return QIcon(":/images/Parameters.png"); }
  };

  /**
   * \class HejnowiczSurfaceGrowth HejnowiczSurface.hpp <HejnowiczSurface.hpp>
   *
   * Process to grow a Hejnowicz surface. It requires a HejnowiczSurfaceParms 
   * process and a TissueParms process to be defined and their names passed 
   * through the uniform interface.
   *
   * \ingroup GrowingSurfaceProcesses
   */
  class mgxBase_EXPORT HejnowiczSurfaceGrowth : public Process 
  {
  public:
    HejnowiczSurfaceGrowth(const Process &process) : Process(process) {}

    /// Initialize the surface
    bool initialize(QStringList &parms, QWidget *parent);

    /// Run a step of the growth
    bool step(const QStringList &parms);

    /// Rewind the surface
    bool rewind(QStringList &parms, QWidget *parent);

    /// Process long description
    QString description() const { return "Growing hejnowicz surface"; }

    // Parameters
    enum ParmNames { pDt, pDrawSteps, pCellKill, pTissueParmsProc, pSurfaceParmsProc, pNumParms }; 

    QStringList parmNames() const 
    {
      QVector <QString> vec(pNumParms);

      vec[pDt] = "Dt";
      vec[pDrawSteps] = "DrawSteps";
      vec[pCellKill] = "CellKill";
      vec[pTissueParmsProc] = "Tissue Parms Process";
      vec[pSurfaceParmsProc] = "Surface Parms Process";

      return vec.toList();
    }

    QStringList parmDescs() const 
    {
      QVector <QString> vec(pNumParms);

      vec[pDt] = "Growth timestep";
      vec[pDrawSteps] = "Steps between drawn frames";
      vec[pCellKill] = "Distance from the tip (in NRS coordinates) to kill cell";
      vec[pTissueParmsProc] = "Process to hold tissue parameters";
      vec[pSurfaceParmsProc] = "Process to hold surface parameters";

      return vec.toList();
    }

    QStringList parmDefaults() const 
    {
      QVector <QString> vec(pNumParms);

      vec[pDt] = "0.1";
      vec[pDrawSteps] = "3";
      vec[pCellKill] = "3.0"; 
      vec[pTissueParmsProc] = "TissueParms";
      vec[pSurfaceParmsProc] = "SurfaceParms";     

      return vec.toList();
    }

    ParmChoiceMap parmChoice() const 
    {
      ParmChoiceMap map;
      return map;
    }

    // Plug-in icon
    QIcon icon() const { return QIcon(":/images/Hejnowicz.png"); }

    // Hejnowicz surface object
    HejnowiczSurface hejnowiczSurface;    

  private:
    // Read parameters
    bool processParms(const QStringList &parms);
        
    // Model parameters from GUI
    double dt;                 // Timestep
    int drawSteps;             // Steps per GUI update
    double cellKill;           // Arclength to kill cells at

    // Mesh object
    Mesh *mesh;         // Current mesh
    CellTissue *T;      // Cellular tissue

    // Hejnowicz vertex atttributes
    HejnowiczSurface::VertexAttr *vertexData; 

    QString surfaceParmsProc; // Process name for the surface parameters
    QStringList surfaceParms;

    QString tissueParmsProc; // Process name for the tissue parameters
    QStringList tissueParms;

    // Define all global data you want to save in the mesh in the attributes 
    double &time() 
    { 
      return mesh->attributes().attrMap<QString, double>("HejnowiczSurface Time")["Time"]; 
    }
  };

  /**
   * \class HejnowiczInitialCell HejnowiczSurface.hpp <HejnowiczSurface.hpp>
   *
   * Process to create an initial cell for a Hejnowicz surface. In this class name() 
   * is not defined (and is virtual), so you will need to derive your own version of it.
   *
   * \ingroup GrowingSurfaceProcesses
   */
  class mgxBase_EXPORT HejnowiczInitialCell : public Process 
  {
  public:
    HejnowiczInitialCell(const Process &process) : Process(process) {}

    /// Make the initial cell
    bool run(const QStringList &parms) 
    {
      Mesh *mesh = currentMesh();
      Point2d top, bottom, middle; 
      fromQString(top, parms[1]);
      fromQString(bottom, parms[2]);
      fromQString(middle, parms[3]);
      return run(mesh, parms[0], top, bottom, middle);
    }
    bool run(Mesh *mesh, const QString &growthProc, 
                 const Point2d &top, const Point2d &bottom, const Point2d &middle);

    // Functions for Gui
    QString description() const { return "Create initial cell for Hejnowicz model"; }
    QStringList parmNames() const { return QStringList() 
      << "Hejnowicz Growth Process" << "Top uv" << "Bottom uv" << "Middle uv"; }
    QStringList parmDescs() const { return QStringList() 
      << "Hejnowicz Growth Process"
      << "Local coordinates for top corner (mirrored)"
      << "Local coordinates for bottom corner (mirrored)"
      << "Local coordinates for bottom middle point"; }
    QStringList parmDefaults() const { return QStringList() 
      << "HejnowiczSurfaceGrowth" << "0.7 0.5" << "0.2 0.7" << "0.0 0.7"; }

    // Icon file
    QIcon icon() const { return QIcon(":/images/InitialCell.png"); }

  private:
    QStringList hejnowiczGrowthParms;
    HejnowiczSurfaceGrowth *hejnowiczGrowth;
  };
 }  
#endif

