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

/**
 * \file CellTissue.hpp
 *
 * This file contains the definition of the cellular tissue
 */

#include <Config.hpp>
#include <Types.hpp>
#include <Information.hpp>
#include <Attributes.hpp>
#include <Subdivide.hpp>
#include <Geometry.hpp>
#include <DistObject.hpp>

namespace mgx 
{
  class Mesh;

  // Parameter names for the various mesh types
  struct mgx_EXPORT CellTissueMgx2d
  {
    enum ParmNames { pCellUpdGeomMode,
      pCellWallWidth, pCellWallCorner, pCellColorBegin, pCellColorEnd, pCellColorCenter, pCellBorderColor,
      pCellPolWidth, pCellPolCorner, pCellPolColorBegin, pCellPolColorEnd, pCellNormalColor, pCellGraphColor,
      pCellLineColor, pCellLineWidth, pNumParms };
  };
	  
  struct mgx_EXPORT CellDivideMgx2d
  {
    enum ParmNames { pCellMaxArea, pCellDivAlg, pCellWallSample, pCellPinch, pCellMaxPinch, pCellWallMin, pNumParms };
  };

  class mgx_EXPORT CellTissue
  {
  public:
    /**
     * Type of cell division
     * SHORTEST WALL  :
     * CLOSEST MID    : take midpoints of all wall segments and check which one is the closest to the center vertex of the cell
     * CLOSEST WALL   :
     * SHORTEST DIR   : test various directions (through the cell center) and divide along the shortes dir
     * PREDEFINED DIR :
     */
    enum DivAlg {SHORTEST_WALL = 0, CLOSEST_MID = 1, CLOSEST_WALL = 2, SHORTEST_DIR = 3, PREDEFINED_DIR = 4};

    /**
     * Type of update to geoemtry
     */
    enum UpdGeom {UPD_AREA = 1, UPD_LENGTH = 2, UPD_CENTER = 4, UPD_NORMAL = 8, UPD_VOLUME = 16, UPD_WALLSZ = 32};

    vvGraph S;      // All the vertices
    vvGraph J;      // The junction (plus centers) graph
    cellGraph C;    // Cell centers

    // Distributed neighborhood objects
    typedef DistNhbd<vvGraph> DistNhbdS;
    DistNhbdS nhbdS;
    typedef DistNhbd<cellGraph> DistNhbdC;
    DistNhbdC nhbdC;

    // Constructor
    CellTissue() : nhbdS(S), nhbdC(C), CellMaxArea(25), CellDivAlg(SHORTEST_WALL), 
      CellWallSample(.05), CellPinch(.2), CellMaxPinch(1.0), CellWallMin(.1), CellUpdGeomMode(63), 
      CellWallWidth(0.025), CellWallCorner(0.15), CellColorBegin(1), CellColorEnd(2),
      CellColorCenter(0.3), CellBorderColor(0), CellPolWidth(0.0), CellPolCorner(0.3), CellPolColorBegin(3),
      CellPolColorEnd(4), CellNormalColor(5), CellGraphColor( 5), CellLineColor(5), CellLineWidth(1.0),
      _meshType("MGXM"), _currLabel(0) {}

    /**
     * Returns a reference to the mesh type
     *
     * Types:
     *   MGXM - normal mesh
     *   MGXC - cell mesh
     *   MGX2D - cell mesh plus cell graph
     *   MGX3D - 3D cellular mesh
     */
    const QString &meshType() { return _meshType; }

    const QString &setMeshType(const QString &meshType);

    /**
     * Returns the current label, without modifying it
     */
    int viewLabel() const { return _currLabel; }

    /**
     * Sets the current label
     */
    void setLabel(int l) { _currLabel = l; }

    /**
     * Increment the current label and return
     */
    int nextLabel() { return ++_currLabel; }

    /**
     * Check the graph for consistency
     */
    bool chkGraph(const vvGraph &S) const;

    /**
     * Set area, length of cell walls, center and normal
     */
    void updGeometry() { updGeometry(CellUpdGeomMode); }
    void updGeometry(int mode);

    /**
     * Set area, length of cell walls, center and normal
     */
    void updGeometry(const cell &c) { updGeometry(c, CellUpdGeomMode); }
    void updGeometry(const cell &c, int mode);
    bool hasCellGraph();   

    /**
      * Map a string to the enum
      */
    static DivAlg stringToDivAlg(const QString &s)
    {
      if(s.toUpper() == "CLOSEST MIDPOINT") 
        return CLOSEST_MID;
      else if(s.toUpper() == "CLOSEST WALL") 
        return CLOSEST_WALL;
      else if(s.toUpper() == "SHORTEST DIRECTION") 
        return SHORTEST_DIR;
      else if(s.toUpper() == "PREDEFINED DIRECTION") 
        return PREDEFINED_DIR;
      // Shortest wall is the default
      return SHORTEST_WALL;
    }

    /**
     * Divide a cell
     */
    bool divideCell(const cell &c, Subdivide *sDiv, DivAlg divAlg = SHORTEST_WALL);

    /**
     * Divide a cell with a list of possible directions (only required for SHORTEST_DIR)
     */
    bool divideCell(const cell &c, Subdivide *sDiv, DivAlg divAlg, std::vector<Point3d> &nrmls);

    /**
     * Delete a cell from the graph
     */
    void deleteCell(const cell &c);

    /**
     * Read the parameters for the cell tissue from MGX GUI
     */
    void processParms(const QStringList &parms);
    void processParmsDivide(const QStringList &parms);

    /**
     * Add a cell to the mesh, the first position is the center
     */
    bool addCell(std::vector<vertex> poly);

    /**
     * Create a normal MorphoGraphX mesh (from any other type)
     */
    bool toMgxm();

    /**
     * Generate a simplified cell mesh (MGXC) from a normal (MGXM) one
     * or from MGX2D
     */
    bool toMgxc(double wallMax = 0);

    /**
     * Create a 2D cellular tissue, from MGXC
     */
    bool toMgx2d(double wallMax = 0);
    bool createTissueGraphs(vvGraph &cS,vvGraph &cJ,cellGraph & cC,double wallMax = 0);
    /**
     * Create a 3D cellular tissue
     */
    typedef std::pair<int, int> IntInt;
    typedef std::pair<IntInt, double> IntIntDouble;
    void getNeighborhoodMap3D(std::map<IntInt, double>& neighborMap);
    bool toMgx3d(double tolerance = 0, double neighborMinArea = 0);

    /**
     * Create/update all graph from 3D cell tissue (stored in S)
     */
    void updateAllGraph();

    /**
     * See if mesh is a cell mesh (MGXM), and if so, correct the vertex types 
     */
    //bool correctCellMesh(const vvGraph &S);

    /**
      * Read cell mesh from a text file.
      */
    bool loadCellTissueFromText(const QString &fileName);

    /**
      * Save cell mesh from a text file.
      */
    bool saveCellTissueToText(const QString &fileName);

    /**
      * Return a vertex from the cell
      */
    inline cell getCell(vertex v)
    {
      cell c(0);
      if(v and v->cId)
        c = cell(v->cId);
      return c;
    } 

    /**
      * Return a vertex from the cell
      */
    inline vertex getVtx(cell c)
    {
      vertex v(0);
      if(c and c->vId)
        v = vertex(c->vId);
      return v;
    }
    inline bool assocCellVertex(vertex &v, cell &m)
    {  
      //if(v->saveId == 0 or m->saveId == 0) //TODO: ErrorCheck
      //  return false;
      m->vId = v.id();
      v->cId = m.id();
      return true;
    }

    // Parameters for cell division, that have to be accessed from outside
    // RSS: Make these methods if required
    float CellMaxArea;    // Area threshold for cell division
    DivAlg CellDivAlg;    // Cell division algorithm

  private:
    // Apply Min distance from corner and return difference
    double findWallMin(Point3d &v, Point3d v1, Point3d v2, double mw);
    
    // Find Cell divide point
    bool findCellDiv(DivAlg divAlg, cell c, Point3d &pu, Point3d &pv, vertex &p_u1, vertex &p_u2, 
        vertex &p_v1, vertex &p_v2, double &su, double &sv, std::vector<Point3d> &nrmls);

    // Find Cell divide point 3D
    bool findCellDiv3D(QString divAlg, cell c, Point3d &pu, Point3d &pv, vertex &p_u1, 
    vertex &p_u2, vertex &p_v1, vertex &p_v2, double &su, double &sv, std::vector<Point3d> &nrmls);

    // Parameters from parms file
    double CellWallSample;  // Sample size for cell division
    double CellPinch;    // Amount to pinch at division
    double CellMaxPinch;    // Max amount to pinch at division
    double CellWallMin;    // Min distance to avoid 4-way junction
    int CellUpdGeomMode;  // What to update when calling updGeometry()

    double CellWallWidth;    // Width of lines for cell walls
    double CellWallCorner;  // Size of corner triangles
    int CellColorBegin;  // Beginning of cell face color ramp
    int CellColorEnd;  // End of cell face color ramp
    double CellColorCenter; // Amount that color increases towards the center
    int CellBorderColor; // Color to draw borders

    double CellPolWidth; // Width to draw cell polarity
    double CellPolCorner; // Size of cell polarity corner triangles
    int CellPolColorBegin; // Start of polarity color ramp
    int CellPolColorEnd; // End of polarity color ramp

    int CellNormalColor; // Color to draw normals
    int CellGraphColor; // Color to draw cell graph
    int CellLineColor; // Color to draw lines
    double CellLineWidth; // Width for all lines

    // Mesh Type
    QString _meshType;
    int _currLabel;
  };
}

#endif
