#include <igl/readOBJ.h>
#include <igl/writeOBJ.h>
#include <igl/snap_points.h>

#include <cube_style_data.h>
#include <cube_style_precomputation.h>
#include <cube_style_single_iteration.h>
#include <normalize_unitbox.h>

#include <ctime>
#include <vector>
#include <iostream>
#include <fstream>

#define DEBUG

// to run the code, type "./cubeStyle_bin [meshName] [lambda]"
// [meshDirectory] [lambda] [iterations] [stopping crit.] [has constrains] [constraint file]

int main(int argc, char *argv[])
{
	using namespace Eigen;
	using namespace std;
	
	MatrixXd V, U;
	MatrixXi F;
    cube_style_data data;

	string obj = argv[1];
	data.lambda = stod(argv[2]);
	int maxIter = stoi(argv[3]);
	double stopReldV = stod(argv[4]);
	bool hasConstraints = stoi(argv[5]);
	
	//Print Arguments:
	#ifdef DEBUG
	cout << "Mesh: " << obj << endl;
	cout << "Lambda: " << data.lambda << endl;
	cout << "Max Iterations: " << maxIter << endl;
	cout << "Stopping Criteria: " << stopReldV << endl;
	#endif
	
	igl::readOBJ(obj, V, F);
	//Normalize BBOX is now done in HOUDINI (may offer interesting control if one wants to try and break it.

	RowVector3d meanV = V.colwise().mean();
	V = V.rowwise() - meanV;
	U = V;

    // set a constrained point F(0,0)
	if (hasConstraints == 0) {
        data.bc.resize(1,3);
        data.bc << V.row(F(0,0));

        data.b.resize(1);
        data.b << F(0,0);
    }
	else {
		string constraintFile = argv[6];
		
		cout << constraintFile;
		
		vector<int> pointNumbers;
		int size;
		
		//Load file.
		ifstream file(constraintFile, ios::in | ios::binary);
		
		file.read((char*)&size, sizeof(size) );
		
		pointNumbers.resize(size);
		
		file.read((char *)pointNumbers.data(), size * sizeof(int));
		file.close();
		
		data.bc.resize(size, 3);
		
		data.b.resize(size);

		for(int i = 0; i < pointNumbers.size(); i++) {
			data.bc.row(i) = V.row(pointNumbers[i]);
			data.b(i) = pointNumbers[i];
		}
	}

    // precomputation ARAP and initialize ADMM parameters
    cube_style_precomputation(V,F,data);

    for (int iter=0; iter<maxIter; iter++)
    {
        cout << "iteration: " << iter << endl;
        cube_style_single_iteration(V,U,data);
        if (data.reldV < stopReldV) {
			break;
		}
    }

	//Overwrite the input mesh.
	igl::writeOBJ(obj,U,F);

    return 0;
}