//==============================================================================
/*! \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/27                         \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 loads a file from input channel
 * - Then, smooths it according to input arguments
 * - Result is written into output channel
 */

#include "SmoothMesh.h"

#include <OMToolkit\IO\OMIO.h>
#include <OMToolkit\OMTypes.h>
//#include <minmax.h>
//#include <OpenMesh\Tools\Smoother\LaplaceSmootherT.hh>
#include <OpenMesh\Tools\Smoother\JacobiLaplaceSmootherT.hh>
#include <OMToolkit\OMSmoother.h>

///////////////////////////////////////////////////////////////////////////////////////////////////
// Module constants
///////////////////////////////////////////////////////////////////////////////////////////////////

// Module description
const std::string MODULE_DESCRIPTION    = "Module that smooths mesh data";

// Additional command line arguments
const std::string MODULE_ARGUMENTS      = "method:iterations:continuity:smooth:alpha:gauss";

// Additional arguments
const std::string MODULE_ARG_METHOD			= "method";
const std::string MODULE_ARG_ITERATIONS		= "iterations";
const std::string MODULE_ARG_CONTINUITY		= "continuity";
const std::string MODULE_ARG_SMOOTH			= "smooth";
const std::string MODULE_ARG_ALPHA			= "alpha";
const std::string MODULE_ARG_GAUSS			= "gauss";

// Smoothing method possibilities
const std::string JACOBI					= "jacobi";
const std::string LOCAL						= "local";
const std::string METHOD_DEFAULT			= JACOBI;

// Iterations possibilities
const int DEFAULT_ITERATIONS				= 1;

// Continuity possibilities
const std::string CONTINUITY_C0				= "c0";
const std::string CONTINUITY_C1				= "c1";
const std::string CONTINUITY_C2				= "c2";
const std::string CONTINUITY_DEFAULT		= CONTINUITY_C1;

// Smooth component possibilities
const std::string SMOOTH_TANGENTIAL				= "tangential";
const std::string SMOOTH_NORMAL					= "normal";
const std::string SMOOTH_BOTH					= "both";
const std::string SMOOTH_DEFAULT				= SMOOTH_BOTH;

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

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

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

///////////////////////////////////////////////////////////////////////////////////////////////////
// Do on startup
///////////////////////////////////////////////////////////////////////////////////////////////////
bool OMSmoothMesh::startup()
{
	// Disable OpenMesh logging
	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;
    }

	// Test existence of method specification (now, only one is allowed)
	m_method = METHOD_DEFAULT;
	m_Arguments.value(MODULE_ARG_METHOD, m_method);
	if (m_method != JACOBI && m_method != LOCAL)
	{
		MDS_CERR('<' << m_sFilename << "> Bad parameter value (-method): type -h for help" << std::endl);
        printUsage();
        return false;
	}

	// Test existence of iterations specification
	m_iterations = DEFAULT_ITERATIONS;
	m_Arguments.value(MODULE_ARG_ITERATIONS, m_iterations);
	if (m_iterations <= 0)
	{
		MDS_CERR('<' << m_sFilename << "> Bad parameter value (-iterations): type -h for help" << std::endl);
        printUsage();
        return false;
	}

	// Test existence of continuity specification
	m_continuity = CONTINUITY_DEFAULT;
	m_Arguments.value(MODULE_ARG_CONTINUITY, m_continuity);
	if (m_continuity != CONTINUITY_C0 && m_continuity != CONTINUITY_C1 && m_continuity != CONTINUITY_C2)
	{
		MDS_CERR('<' << m_sFilename << "> Bad parameter value (-continuity): type -h for help" << std::endl);
        printUsage();
        return false;
	}
	
	// Test existence of smooth argument
	m_smooth = SMOOTH_DEFAULT;
	m_Arguments.value(MODULE_ARG_SMOOTH, m_smooth);
	if (m_smooth != SMOOTH_TANGENTIAL && m_smooth != SMOOTH_NORMAL && m_smooth != SMOOTH_BOTH)
	{
		MDS_CERR('<' << m_sFilename << "> Bad parameter value (-smooth): type -h for help" << std::endl);
        printUsage();
        return false;
	}

	m_alpha = 0.0;
	m_Arguments.value(MODULE_ARG_ALPHA, m_alpha);
	if (m_method == LOCAL && m_alpha == 0.0)
	{
		MDS_CERR('<' << m_sFilename << "> Bad parameter value (-alpha): type -h for help" << std::endl);
        printUsage();
        return false;
	}

	m_gauss = 1.5;
	m_Arguments.value(MODULE_ARG_GAUSS, m_gauss);
	if (m_method == LOCAL && m_gauss <= 0.0)
	{
		MDS_CERR('<' << m_sFilename << "> Bad parameter value (-gauss): type -h for help" << std::endl);
        printUsage();
        return false;
	}


	
    // O.K.
    return true;
}

///////////////////////////////////////////////////////////////////////////////////////////////////
// Main module loop
///////////////////////////////////////////////////////////////////////////////////////////////////
bool OMSmoothMesh::main()
{
    //// Note
    MDS_LOG_NOTE("Module main function");

    //// I/O channels
    mds::mod::CChannel *pIChannel = getInput(0);
    mds::mod::CChannel *pOChannel = getOutput(0);

	 // Is any input?
    if( !pIChannel->isConnected() )
    {
        return false;
    }

    // Wait for data
    if( pIChannel->wait(1000) )
    {
		// Mesh specification and read options
		MeshT mesh;
		OMToolkit::IO::Options opt = OMToolkit::IO::Options::Default;
	
		// Read and save mesh
		if (OMToolkit::IO::readMesh(mesh, *pIChannel, opt))
		{
			using namespace OpenMesh::Smoother;
			SmootherT<MeshT>::Continuity continuity;
			SmootherT<MeshT>::Component component;
			
			// set continuity
			if (m_continuity == CONTINUITY_C0)
				continuity = SmootherT<MeshT>::C0;
			else if (m_continuity == CONTINUITY_C1)
				continuity = SmootherT<MeshT>::C1;
			else if (m_continuity == CONTINUITY_C2)
				continuity = SmootherT<MeshT>::C2;

			// set smoothing
			if (m_smooth == SMOOTH_BOTH)
				component = SmootherT<MeshT>::Tangential_and_Normal;
			else if (m_smooth == SMOOTH_TANGENTIAL)
				component = SmootherT<MeshT>::Normal;
			else if (m_smooth == SMOOTH_NORMAL)
				component = SmootherT<MeshT>::Tangential;

			// do smoothing
			if (m_method == JACOBI)
			{
				//OMToolkit::smoother2 smoother2(&mesh);
				//smoother2.initialize(component, continuity);
				//smoother2.smooth(m_iterations);

				JacobiLaplaceSmootherT<MeshT> smoother(mesh);
				smoother.initialize(component, continuity);
				smoother.smooth(m_iterations);
			}
			////////////////////////////////////////////////////////////////////////////////////////////////////////////
			else if (m_method == LOCAL)
			{
				MeshT::VertexIter end = mesh.vertices_end();
				for (MeshT::VertexIter vertex = mesh.vertices_begin(); vertex != end; ++vertex)
				{
					mesh.curvatureMagnitude(vertex) = mesh.getAttributes(vertex)[0];
				}

				OMToolkit::OMSmoother smoother(&mesh, mesh.getCurvatureHandle(), mesh.getCurvatureMagHandle(), m_gauss);
				smoother.smooth(m_iterations, m_alpha);
			}
			////////////////////////////////////////////////////////////////////////////////////////////////////////////

			if (!OMToolkit::IO::writeMesh(mesh, *pOChannel))
			{
				MDS_CERR('<' << m_sFilename << "> Failed to write output mesh data" << std::endl);
				return false;
			}
		}
		// Error on input
		else 
		{
			MDS_CERR('<' << m_sFilename << "> Failed to read input mesh data" << std::endl);
			return false;
		}

		return false;
	}
    else
    {
        MDS_LOG_NOTE("Wait timeout");
    }

    // Returning 'true' means to continue processing the input channel
    return true;
}

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

///////////////////////////////////////////////////////////////////////////////////////////////////
// Writes extended use of this module
///////////////////////////////////////////////////////////////////////////////////////////////////
void OMSmoothMesh::writeExtendedUsage(std::ostream& Stream)
{
    MDS_CERR("Necessary arguments: [-method method] [-iterations no] [-continuity cont] [-smooth component]" << std::endl);
    MDS_CERR("Options:" << std::endl);
	MDS_CERR("  -method Specifies smoothing method. Possibilities:" << std::endl);
	MDS_CERR("		jacobi : At this time, only one method is allowed, Jacobi-Laplace smoothing" << std::endl);
	MDS_CERR("		locat : Testing method smoothing" << std::endl);
	MDS_CERR("		DEFAULT: jacobi" << std::endl);
	MDS_CERR("  -iterations Specifies number of smoothing iterations [1..inf]. Possibilities:" << std::endl);
	MDS_CERR("		DEFAULT: 1" << std::endl);
	MDS_CERR("  -continuity Specifies smoothing continuity. Possibilities:" << std::endl);
	MDS_CERR("		c0 : C0 continuity" << std::endl);
	MDS_CERR("		c1 : C1 continuity" << std::endl);
	MDS_CERR("		c2 : C2 continuity" << std::endl);
	MDS_CERR("		DEFAULT: C1" << std::endl);
	MDS_CERR("  -smooth Specifies, which component will be smoothed. Possibilities:" << std::endl);
	MDS_CERR("		normal : Smooth normal direction." << std::endl);
	MDS_CERR("		tangential : Smooth tangential direction." << std::endl);
	MDS_CERR("		both : Smooth tangential and normal direction." << std::endl);
	MDS_CERR("		DEFAULT: both" << std::endl);
    MDS_CERR(std::endl);
}

///////////////////////////////////////////////////////////////////////////////////////////////////
// Main - executing a module
///////////////////////////////////////////////////////////////////////////////////////////////////
int main(int argc, char *argv[])
{
    // Creation of a module using smart pointer
    OMSmoothMeshPtr spModule(new OMSmoothMesh(MODULE_DESCRIPTION));

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

    // Console application finished
    return 0;
}

