//
// 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.
//
#include <MeshProcessImport.hpp>
#include <MeshProcessSignal.hpp>
#include <CImg.h>
using namespace cimg_library;

namespace mgx
{
  bool Load3DFrom2View::run(Mesh *mesh, const QString &heightFileName, const QString &colorFileName, Point3d scale)
  {
    if(!mesh)
      throw QString("%1::run No current mesh").arg(name());
    if(scale.x() <= 0 or scale.y() <= 0 or scale.z() <= 0)
      throw QString("%1::run Invalid scale").arg(name());

    vvGraph &S = mesh->graph();
  	S.clear();

    QFile heightFile(heightFileName);
    if(!heightFile.open(QIODevice::ReadOnly | QIODevice::Text))
      throw QString("%1::run Cannot open height input file: %2").arg(name()).arg(heightFileName);
    QTextStream heightStream(&heightFile);

    QFile colorFile(colorFileName);
    //if(!colorFile.open(QIODevice::ReadOnly | QIODevice::Text))
      //throw QString("%1::run Cannot open color input file: %2").arg(name()).arg(colorFileName);
    //QTextStream colorStream(&colorFile);
    CImg<ushort> img(colorFileName.toStdString().data());

    // Get the size
    int ysz = 1;
    QStringList fields = heightStream.readLine().split(",");
    int xsz = fields.size();
    while(!heightStream.atEnd()) {
      heightStream.readLine();
      ysz++;
    }
    Information::out << QString("Image size is %1 x %2").arg(xsz).arg(ysz) << endl;

    heightStream.seek(0);
  
    // Save the vertex ids for connecting
    std::vector<std::vector<vertex> > vtx(xsz, std::vector<vertex>(ysz, vertex(0)));
    
    for(int y = 0; y < ysz; y++) { //line number
      if(heightStream.atEnd())
        throw QString("%1::run Premature end of file reading heights").arg(name());
      //if(colorStream.atEnd())
        //throw QString("%1::run Premature end of file reading colors").arg(name());
    
      QStringList heights = QString(heightStream.readLine()).split(",");  
      if(heights.size() != xsz)
        throw QString("%1::run Incorrect field count %2 in height file on line %3").arg(name()).arg(heights.size()).arg(y);
      //QStringList colors = QString(colorStream.readLine()).split(",");  
      //if(colors.size() != xsz)
        //throw QString("%1::run Incorrect field count %2 in color file on line %3").arg(name()).arg(heights.size()).arg(y);

      for(int x = 0; x < xsz; ++x) {
        double z = heights[x].toDouble();
        //double c = colors[xsz - x - 1].toDouble();
        double c = img(x, y);

        // Check for NaN
        if(!(z == z))
  	      z = 0;
        
        // Create the vertices and fill in height and texture coords
        vertex v;
  
        S.insert(v);
        vtx[x][y] = v; 
        v->pos.x() = double((x - xsz/2)*scale.x());
        v->pos.y() = double((y - ysz/2)*scale.y());
        v->pos.z() = z*scale.z();
        v->label = 0;
        v->signal = -c;
      }
    }

    // Connect neighborhoods
    for(int y = 0; y < ysz; y++) 
      for(int x = 0; x < xsz; x++) {
        std::vector<vertex> nhbs;
        nhbs.push_back(vtx[x][y]);
        if(x == 0 and y == ysz - 1) {			// top left corner;
          nhbs.push_back(vtx[x][y-1]);
          nhbs.push_back(vtx[x+1][y-1]);
          nhbs.push_back(vtx[x+1][y]);
        } else if(x == 0 and y == 0) {		// bottom left corner
          nhbs.push_back(vtx[x+1][y]);
          nhbs.push_back(vtx[x][y+1]);
        } else if(x == xsz - 1 and y == 0) {		// bottom right corner
          nhbs.push_back(vtx[x][y+1]);
          nhbs.push_back(vtx[x-1][y+1]);
          nhbs.push_back(vtx[x-1][y]);
        } else if(x == xsz - 1 and y == ysz - 1) {	// top right corner
          nhbs.push_back(vtx[x-1][y]);
          nhbs.push_back(vtx[x][y-1]);
        } else if(y == ysz - 1) {			// top edge
          nhbs.push_back(vtx[x-1][y]);
          nhbs.push_back(vtx[x][y-1]);
          nhbs.push_back(vtx[x+1][y-1]);
          nhbs.push_back(vtx[x+1][y]);
        } else if(x == 0) {				// left edge
          nhbs.push_back(vtx[x][y-1]);
          nhbs.push_back(vtx[x+1][y-1]);
          nhbs.push_back(vtx[x+1][y]);
          nhbs.push_back(vtx[x][y+1]);
        } else if(y == 0) {				// bottom edge
          nhbs.push_back(vtx[x+1][y]);
          nhbs.push_back(vtx[x][y+1]);
          nhbs.push_back(vtx[x-1][y+1]);
          nhbs.push_back(vtx[x-1][y]);
        } else if(x == xsz - 1) {			// right edge
          nhbs.push_back(vtx[x][y-1]);
          nhbs.push_back(vtx[x-1][y+1]);
          nhbs.push_back(vtx[x-1][y]);
          nhbs.push_back(vtx[x][y+1]);
        } else {					// Interior vertex
          nhbs.push_back(vtx[x+1][y-1]);
          nhbs.push_back(vtx[x+1][y]);
          nhbs.push_back(vtx[x][y+1]);
          nhbs.push_back(vtx[x-1][y+1]);
          nhbs.push_back(vtx[x-1][y]);
          nhbs.push_back(vtx[x][y-1]);
        }
        vertex v = nhbs[0];
        vertex pn(0);
        for(uint i = 1; i < nhbs.size(); i++) {
          vertex n = nhbs[i];
          if(i == 1)
            S.insertEdge(v, n);
          else
            S.spliceAfter(v, pn, n);
          pn = n;
        }
      }
    
    mesh->signalUnit() = "";
    mesh->signalBounds() = calcSignalBounds(S);
    mesh->updateAll();

    return true;
  }
  REGISTER_PROCESS(Load3DFrom2View);
}
