//
// 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.
//

// Implement surface and surface point class
#include <iostream>
#include <cstdio>
#include <cmath>

#include <DynamXProcessBezierSurface.hpp>
#include <Function.hpp>
#include <Contour.hpp>
#include <Information.hpp>

namespace mgx
{
  // Initialize the surface
  bool BezierSurfaceGrowth::initialize(QStringList &parms, QWidget *parent)
  {
    // If there is parent, then we are in gui mode
    //if(parent) ;

    // Set the static class variables each time
    mesh = currentMesh();
    T = &mesh->tissue();

    // Read the parms from the GUI
    processParms(parms);

    // The first time in create the mesh
    if(T->S.size() == 0)
      rewind(parms, parent);
    else if(T->meshType() == "MGX2D") {
      // If we have an incoming mesh, make sure coordinates are OK
      forall(vertex v, T->S)
        bezierSurface.setPoint(v, v, v->pos);
    } else
      throw(QString("Invalid mesh type, must be a 2D cellular mesh"));

    return true;
  } 

  bool BezierSurfaceGrowth::rewind(QStringList &parms, QWidget *parent)
  {
    // Reset the mesh and create an initial cell
    mesh->reset();

    // Re-process the parms
    processParms(parms);

    // Set the mesh type
    mesh->setMeshType("MGX2D");

    // Create initial cell
    bezierSurface.initialCell(*T, cellInitSize, cellInitWalls);

    // Find center and set cell area and length of cell walls
    T->updGeometry(); 

    // Propagate cell center to vertex
    forall(const cell &c, T->C) {
      vertex v = T->getVtx(c);
      bezierSurface.setPoint(v, v, v->pos);
    }

    forall(const cell &c, T->C)
      Information::out << "Initial Cell Area " << c->area << endl;

    // Update GUI
    mesh->updateAll();    
    return true;
  } 

  // Process model parameters
  bool BezierSurfaceGrowth::processParms(const QStringList &parms)
  {
    // Get the attribute maps
    vertexAttr = &mesh->attributes().attrMap<vertex, BezierSurface::VertexData>("BezierSurfaceVertexData");

		// Get model parms
    dt = parms[pDt].toDouble();
    drawSteps = parms[pDrawSteps].toInt();
    cellInitWalls = parms[pCellInitWalls].toInt();
    cellInitSize = parms[pCellInitSize].toDouble();
    QString surfaceParmsProc = parms[pSurfaceParmsProc];
    QString tissueParmsProc = parms[pTissueParmsProc];

    QStringList surfaceParms;
    if(!getLastParms(surfaceParmsProc, surfaceParms))
      throw(QString("Cannot get surface parameters"));
    bezierSurface.initialize(vertexAttr, surfaceParms);

    QStringList tissueParms;
    if(!getLastParms(tissueParmsProc, tissueParms))
      throw(QString("Cannot get tissue parameters"));
    T->processParms(tissueParms);

    return true;
  }

  // Main model step
	bool BezierSurfaceGrowth::step(const QStringList &parms)
  {    
    for(int i = 0; i < drawSteps; i++) {
      // Advance time
      time() += dt;
  
			// Grow cell edges, avoid the centers
			// 	First grow surface, this is different from polar surface!
			bezierSurface.growSurface(time());
      forall(vertex v, T->S) 
        if(!T->getCell(v)) {
          bezierSurface.updatePos(v);
          bezierSurface.updateNormal(v);
				}

      // Update normals, area, centers, and length of cell walls
      T->updGeometry(); 

      // Now update the center's parametric coordinates 
      forall(const cell &c, T->C) {
        vertex v = T->getVtx(c);
        vertex v1 = T->S.anyIn(v);
        // look for closest point on surface to v, starting from vertex already on surface (v1)
        bezierSurface.setPoint(v, v1, v->pos);
      }
    }
		
		// Update mesh points, edges, surfaces
    mesh->updateAll();   
    return true;
  }
 
}

