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

#include <Surface.hpp>
#include <Function.hpp>

namespace mgx
{
  /**
   * \class HejnowiczSurface HejnowiczSurface.hpp <HejnowiczSurface.hpp>
   *
   * Growing surface using a local coordinate system (NRS) as 
   * defined in Hejnowicz and Karczewski 1993.
   *
   * \ingroup GrowingSurfaceProcesses
   */
  class mgxBase_EXPORT HejnowiczSurface : public Surface
  {
  public:
    /// Parameter names as in Hejnowicz and Karczewski 1993
    enum ParmNames { puS, pvR, pA, pC, pD, pScale, pLength, pGrowthScale, pGrowthType, pGrowthFunc, pNumParms };

    /// Hejnowicz vertex attribute data needed for simulations
    struct VertexData
    {
      Point2d nrs;
      VertexData() : nrs(0,0) {}

      bool operator==(const VertexData &other) const
      {
        if(nrs == other.nrs)
          return true;
        return false;
      }
    };
    
    /// Attribute map for vertex data required by the surface
    typedef AttrMap<vertex, VertexData> VertexAttr;
    
    /// Class to define methods for subdivision
    class mgxBase_EXPORT Subdivide : virtual public mgx::Subdivide
    {
    public:
      Subdivide(VertexAttr *vData, HejnowiczSurface *surf) 
          : vertexData(vData), surface(surf) {}

      virtual bool updateEdgeData(vertex l, vertex v, vertex r, double s);

      VertexAttr *vertexData;
      HejnowiczSurface *surface;      
    };

    // ALR: Constructor should fill in default values, like in FemMGXM
    HejnowiczSurface() : growthType(GROWTH_HEJN) {} 
    ~HejnowiczSurface() {} 

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

      vec[puS] = "uS";
      vec[pvR] = "vR";
      vec[pA] = "A";
      vec[pC] = "C";
      vec[pD] = "D";
      vec[pScale] = "Scale";
      vec[pLength] = "Length";
      vec[pGrowthScale] = "Growth Scale";
      vec[pGrowthType] = "Growth Type";
      vec[pGrowthFunc] = "Growth Function";
  
      return vec.toList();
    }

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

      vec[puS] = "uS";
      vec[pvR] = "vR";
      vec[pA] = "A";
      vec[pC] = "C";
      vec[pD] = "D";
      vec[pScale] = "Surface scaling";
      vec[pLength] = "Surface length";
      vec[pGrowthScale] = "Growth scale";
      vec[pGrowthType] = "Growth type";
      vec[pGrowthFunc] = "Growth function file, clear for growth model 2 from Hejnowicz et al. 1993.";

      return vec.toList();
    }

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

      vec[puS] = "0.45";
      vec[pvR] = "0.785398163"; // PI/4
      vec[pA] = "0.0";
      vec[pC] = "1.0";
      vec[pD] = "0.3";
      vec[pScale] = "1000.0";
      vec[pLength] = "2000.0";
      vec[pGrowthScale] = "1000.0";
      vec[pGrowthType] = "Hejnowicz";
      vec[pGrowthFunc] = "HejnowiczGrowth.func";

      return vec.toList();
    }

    /// Process the parameters from the uniform interface
    bool processParms(const QStringList &parms);

    /// Determine the local coordinates from a 3d position.
    /// A neighboring vertex is used as a hint.
    bool setPoint(vertex p, vertex sp, Point3d cp);

    /// Update the vertex position
    bool updatePos(vertex p);

    /// Update the vertex normal
    bool updateNormal(vertex p);

    /// Create an initial cell
    bool initialCell(CellTissue &T, 
               const Point2d &top, const Point2d &bottom, const Point2d &middle);
    
    // Methods for Hejnowicz surface

    /// Initialize the surface
    bool initialize(VertexAttr *vData, const QStringList &parms);

    /// Grow a surface point
    bool growPoint(vertex p, double dt, double time);

    /// Convert from local (NRS) to Cartesian coordinates
    Point3d localToCartesian(const Point2d &local);

    /// Convert from cartesian to local (NRS) coordinates
    bool cartesianToLocal(const Point3d &cartesian, Point2d &local);

    /// Setup the growth map
    bool setGrowthMap(std::map<double, double> &growthMap);

  private:
    enum GrowthType {GROWTH_HEJN, GROWTH_FUNC, GROWTH_MAP};

    // Integrate growth function
    bool setupGrowth();
    // Function interface into the growth map table, used by integrator.
    double growthMapFunc(double s);

    // vertex attributes required for simulation 
    VertexAttr *vertexData;

    // Parameters as in Hejnowicz and Karczewski 1993
    double uS;
    double vR;
    double A;
    double C;
    double D;
    double scale;
    double length;
    double growthScale;
    QString growthFuncFile;

    GrowthType growthType;
    Function growthFunc;

    double nrsLength; // Length of root domain in NRS coordinates
    std::vector<double> growthTab; // table for map (morphogen) driven growth
    std::vector<double> velocityCart; // speed of point movement in Cartesian coordinates
    std::vector<double> velocityNRS; // speed of point movement in NRS coordinates
  };

  /// Serialize read for Vertex data
  bool inline readAttr(HejnowiczSurface::VertexData &m, const QByteArray &ba, size_t &pos) 
  {
    return readChar((char *)&m, sizeof(HejnowiczSurface::VertexData), ba, pos);
  }
  /// Serialize write for Vertex data
  bool inline writeAttr(const HejnowiczSurface::VertexData &m, QByteArray &ba) 
  {
    return writeChar((char *)&m, sizeof(HejnowiczSurface::VertexData), ba);
  }
}  
#endif

