//==============================================================================
/*! \file
 * OpenMesh Toolkit for mesh analysis    \n
 * Copyright (c) 2010 by Rostislav Hulik     \n
 *
 * Author:  Rostislav Hulik, rosta.hulik@gmail.com  \n
 * Date:    2010/10/25                          \n
 *
 * This file is part of software developed for support of Rostislav Hulik's dissertation thesis at dcgm-robotics@FIT group.
 *
 * This file is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This file is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this file.  If not, see <http://www.gnu.org/licenses/>.
 * 
 * Module description:
 * - Module starts an OSG viewer and shows model, which is in input MDSTk channel
 */

#include "curvature.h"

#include <OpenMesh\Tools\Utils\Timer.hh>
#include <OMToolkit\IO\OMIO.h>

#include <OMToolkit\OMCurvature.h>
#include <OMToolkit\OMTypes.h>
#include <OMToolkit\OMMatrixCurvature.h>
///////////////////////////////////////////////////////////////////////////////////////////////////
// Module constants
///////////////////////////////////////////////////////////////////////////////////////////////////

// Module description
const std::string MODULE_DESCRIPTION    = "Module that computes model curvature";

// Additional command line arguments
const std::string MODULE_ARGUMENTS      = "curvature";

// Additional arguments
const std::string MODULE_ARG_CURVATURE	= "curvature";

// Curvature types
const std::string CURVATURE_MEAN		= "mean";
const std::string CURVATURE_GAUSS		= "gauss";
const std::string CURVATURE_MIN			= "min";
const std::string CURVATURE_MAX			= "max";
const std::string CURVATURE_MATRIXMIN	= "matrixMin";
const std::string CURVATURE_MATRIXMAX	= "matrixMax";
const std::string CURVATURE_MATRIXMEAN	= "matrixMean";
const std::string CURVATURE_MATRIXGAUSS	= "matrixGauss";
const std::string CURVATURE_DEFAULT		= CURVATURE_MEAN;

// Mesh type
typedef OMToolkit::Types::ModuleMeshd			MeshT;

///////////////////////////////////////////////////////////////////////////////////////////////////
// Constructor
///////////////////////////////////////////////////////////////////////////////////////////////////
OMCurvatureMod::OMCurvatureMod(const std::string& sDescription) : mds::mod::CModule(sDescription)
{
    allowArguments(MODULE_ARGUMENTS);
}

///////////////////////////////////////////////////////////////////////////////////////////////////
// Destructor
///////////////////////////////////////////////////////////////////////////////////////////////////
OMCurvatureMod::~OMCurvatureMod()
{
}

///////////////////////////////////////////////////////////////////////////////////////////////////
// Do on startup
///////////////////////////////////////////////////////////////////////////////////////////////////
bool OMCurvatureMod::startup()
{
	// Disable OpenMesh error output
	omlog().disable();
	omerr().disable();
	omout().disable();

    // Note
    MDS_LOG_NOTE("Module startup");

    // Test of existence of input and output channel
    if( getNumOfInputs() != 1 || getNumOfOutputs() != 1 )
    {
		
        MDS_CERR('<' << m_sFilename << "> Wrong number of input and output channels" << std::endl);
        return false;
    }

	m_curvature = CURVATURE_DEFAULT;
	m_Arguments.value(MODULE_ARG_CURVATURE, m_curvature);

    // O.K.
    return true;
}

///////////////////////////////////////////////////////////////////////////////////////////////////
// Main module loop
///////////////////////////////////////////////////////////////////////////////////////////////////
bool OMCurvatureMod::main()
{
	// I/O channels
    mds::mod::CChannel *pIChannel = getInput(0);
	mds::mod::CChannel *pOChannel = getOutput(0);
	MeshT::Normal normal;
    // Is any input?
    if( !pIChannel->isConnected() )
    {
        return false;
    }

    // Wait for data
    if( pIChannel->wait(1000) )
    {
		using namespace OMToolkit;

		// Create a mesh and read it from input channel
		MeshT mesh;
		IO::Options opt;

		if (!IO::readMesh(mesh, *pIChannel, opt))
		{
			MDS_CERR('<' << m_sFilename << "> Failed to read input mesh data" << std::endl);
			return false;
		}

		// init curvature and prime timer
		OMCurvature<MeshT, MeshT::AttributeScalar> curvature(&mesh, mesh.getCurvatureHandle(), mesh.getCurvatureMagHandle());
	
		OpenMesh::Utils::Timer timer;
		timer.reset();
		timer.start();

		// do computation
		if (m_curvature == CURVATURE_MEAN)
		{
			if (!curvature.calcMeanCurvature())
			{
				MDS_CERR('<' << m_sFilename << "> Error in curvature computation. Has input mesh triangular faces?" << std::endl);
				return false;
			}
		}
		else if (m_curvature == CURVATURE_GAUSS)
		{
			if (!curvature.calcGaussCurvature())
			{
				MDS_CERR('<' << m_sFilename << "> Error in curvature computation. Has input mesh triangular faces?" << std::endl);
				return false;
			}
		}
		else if (m_curvature == CURVATURE_MIN)
		{
			if (!curvature.calcMinCurvature())
			{
				MDS_CERR('<' << m_sFilename << "> Error in curvature computation. Has input mesh triangular faces?" << std::endl);
				return false;
			}
		}
		else if (m_curvature == CURVATURE_MAX)
		{
			if (!curvature.calcMaxCurvature())
			{
				MDS_CERR('<' << m_sFilename << "> Error in curvature computation. Has input mesh triangular faces?" << std::endl);
				return false;
			}
		}
		else if (m_curvature == CURVATURE_MATRIXMIN)
		{
			OMToolkit::OMMatrixCurvature curvature(&mesh);
			curvature.Compute(mesh.getMatrixHandle(), mesh.getCurvatureHandle(), mesh.getCurvatureMagHandle(), OMToolkit::OMMatrixCurvature::MIN);
		}
		else if (m_curvature == CURVATURE_MATRIXMAX)
		{
			OMToolkit::OMMatrixCurvature curvature(&mesh);
			curvature.Compute(mesh.getMatrixHandle(), mesh.getCurvatureHandle(), mesh.getCurvatureMagHandle(), OMToolkit::OMMatrixCurvature::MAX);
		}
		else if (m_curvature == CURVATURE_MATRIXMEAN)
		{
			OMToolkit::OMMatrixCurvature curvature(&mesh);
			curvature.Compute(mesh.getMatrixHandle(), mesh.getCurvatureHandle(), mesh.getCurvatureMagHandle(), OMToolkit::OMMatrixCurvature::MEAN);
		}
		else if (m_curvature == CURVATURE_MATRIXGAUSS)
		{
			OMToolkit::OMMatrixCurvature curvature(&mesh);
			curvature.Compute(mesh.getMatrixHandle(), mesh.getCurvatureHandle(), mesh.getCurvatureMagHandle(), OMToolkit::OMMatrixCurvature::GAUSS);
		}
		else
		{
			MDS_CERR('<' << m_sFilename << "> Non existing curvature type." << std::endl);
			return false;
		}

		//show time and write output
		timer.stop();

		std::ofstream file;
		file.open("out.csv", std::ios_base::app);
		file << mesh.n_vertices() << ";" << timer.as_string(OpenMesh::Utils::Timer::MSeconds) << std::endl;
		file.close();
		MDS_LOG_NOTE("Curvature computation of " << mesh.n_vertices() << " vertices successfully ended after " << timer.as_string(OpenMesh::Utils::Timer::MSeconds) << ".");
		
		if (!IO::writeMesh(mesh, *pOChannel))
		{
			MDS_CERR('<' << m_sFilename << "> Failed to write output mesh data" << std::endl);
			return false;
		}
    }
    else
    {
        MDS_LOG_NOTE("Wait timeout");
    }
    // Returning 'true' means to continue processing the input channel
    return false;
}

///////////////////////////////////////////////////////////////////////////////////////////////////
// On module shutdown
///////////////////////////////////////////////////////////////////////////////////////////////////
void OMCurvatureMod::shutdown()
{
    // Note
    MDS_LOG_NOTE("Module shutdown");
}

///////////////////////////////////////////////////////////////////////////////////////////////////
// Writes extended use of this module
///////////////////////////////////////////////////////////////////////////////////////////////////
void OMCurvatureMod::writeExtendedUsage(std::ostream& Stream)
{
	MDS_CERR("Necessary arguments: [-curvature name]" << std::endl);
    MDS_CERR("Options:" << std::endl);
	MDS_CERR("  -curvature Specifies curvature which will be computed. Possibilities:" << std::endl);
	MDS_CERR("		mean: Computes mean curvature" << std::endl);
	MDS_CERR("		gauss: Computes Gauss curvature" << std::endl);
	MDS_CERR("		min: Computes minimum curvature" << std::endl);
	MDS_CERR("		max: Computes maximum curvature" << std::endl);
	MDS_CERR("		matrix: Computes maximum curvature from precomputed matrices" << std::endl);
	MDS_CERR("		DEFAULT: mean" << std::endl);
}

///////////////////////////////////////////////////////////////////////////////////////////////////
// Main - executing a module
///////////////////////////////////////////////////////////////////////////////////////////////////
int main(int argc, char *argv[])
{
    // Creation of a module using smart pointer
    OMCurvatureModPtr spModule(new OMCurvatureMod("Module for viewing a mesh"));

    // Initialize and execute the module
    if( spModule->init(argc, argv) )
    {
        spModule->run();
    }

    // Console application finished
    return 0;
}
