//==============================================================================
/*! \file
 * OpenMesh Toolkit for mesh analysis    \n
 * Copyright (c) 2010 by Rostislav Hulik     \n
 *
 * Author:  Rostislav Hulik, rosta.hulik@gmail.com  \n
 * Date:    2010/11/21                          \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:
 * - The module computes N feature vertices
 * - From the feature points, the SIFT features are computed
 * - Results are saved in the output file
 * - The output mesh has modified curvature magnitude (255 if feature point, 0 else)
 */

#include "ComputeDescriptors.h"


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

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

// Module description
const std::string MODULE_DESCRIPTION    = "Module that computes the interest points and descriptors from the mesh";

// Additional command line arguments
const std::string MODULE_ARGUMENTS      = "feature_count:sift_size:sift_resolution:sift_diameter:filename";

// Additional arguments
const std::string MODULE_ARG_COUNT		= "feature_count";
const std::string MODULE_ARG_SIZE		= "sift_size";
const std::string MODULE_ARG_RES		= "sift_resolution";
const std::string MODULE_ARG_DIAM		= "sift_diameter";
const std::string MODULE_ARG_FILE		= "filename";

const double SIZE_DEFAULT				= 10.0;
const int	 RES_DEFAULT				= 31;
const int	 COUNT_DEFAULT				= 1000;
const double DIAM_DEFAULT				= 31;
const std::string FILE_DEFAULT				= "out.txt";

// Type of accepted mesh
typedef OMToolkit::Types::ModuleMeshd	MeshT;

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

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

///////////////////////////////////////////////////////////////////////////////////////////////////
// Do on startup
///////////////////////////////////////////////////////////////////////////////////////////////////
bool OMComputeDescriptors::startup()
{
	// Disable all OpenMesh errorlogs (for not mix MDSTk log)
	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_size = SIZE_DEFAULT;
	m_resolution = RES_DEFAULT;
	m_count = COUNT_DEFAULT;
	m_diameter = DIAM_DEFAULT;
	m_output_file = FILE_DEFAULT;

	m_Arguments.value(MODULE_ARG_SIZE, m_size);
	m_Arguments.value(MODULE_ARG_DIAM, m_diameter);
	m_Arguments.value(MODULE_ARG_RES, m_resolution);
	m_Arguments.value(MODULE_ARG_COUNT, m_count);
	m_Arguments.value(MODULE_ARG_FILE, m_output_file);

    // O.K.
    return true;
}

///////////////////////////////////////////////////////////////////////////////////////////////////
// Main module loop
///////////////////////////////////////////////////////////////////////////////////////////////////
bool OMComputeDescriptors::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))
		{
			MDS_LOG_NOTE("Starting descriptor computation... Model: " << mesh.n_vertices() << " vertices.");
			OpenMesh::Utils::Timer timer;
			
			// Compute feature points
			OMToolkit::OMMatrixDescriptors descriptors(&mesh);
			descriptors.ComputePoints(mesh.getMatrixHandle(), mesh.getCurvatureMagHandle(), m_count);

			// Compute descriptors
			std::vector<std::vector<float>> desc = descriptors.ComputeDescriptors(mesh.getMatrixHandle(), mesh.getCurvatureMagHandle(), m_size, m_resolution, m_diameter);

			// Each row contains ID of vertex + 128 dimensional feature vector
			std::ofstream out(m_output_file, std::ios_base::out);
			for (unsigned int i = 0; i < desc.size(); ++i)
			{
				std::stringstream output;
				for (unsigned int j = 0; j < desc[i].size(); ++j)
					out << desc[i][j] << " ";
				out << std::endl;
			}

			out.close();

			MDS_LOG_NOTE("... done.");
			// write output
			if (!OMToolkit::IO::writeMesh(mesh, *pOChannel))
			{
				MDS_CERR('<' << m_sFilename << "> Failed to write output 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 OMComputeDescriptors::shutdown()
{
    // Note
    MDS_LOG_NOTE("Module shutdown");
}

///////////////////////////////////////////////////////////////////////////////////////////////////
// Writes extended use of this module
///////////////////////////////////////////////////////////////////////////////////////////////////
void OMComputeDescriptors::writeExtendedUsage(std::ostream& Stream)
{
    MDS_CERR("Necessary arguments: [-feature_count count] [-sift_size size] [-sift_resolution resolution] [-sift_diameter diameter] [-filename filename]" << std::endl);
    MDS_CERR("Options:" << std::endl);
	MDS_CERR("  -count Specifies the count of feature points." << std::endl);
	MDS_CERR("	  -Argument is an integer number greater than 0" << std::endl);
	MDS_CERR("	  -Default value = 1000" << std::endl);
	MDS_CERR("  -size The size of SIFT matrices relative to the median of the edge lengths." << std::endl);
	MDS_CERR("	  -Argument is a double precision number greater than 0.0" << std::endl);
	MDS_CERR("	  -Default value = 10.0" << std::endl);
	MDS_CERR("  -resolution Specifies square matrix dimension in one direction (for SIFT descriptor computation)." << std::endl);
	MDS_CERR("	  -Argument is an integer number greater than 0.0." << std::endl);
	MDS_CERR("	  -Must be odd number (for filtration purposes)" << std::endl);
	MDS_CERR("	  -Default value = 31" << std::endl);
	MDS_CERR("  -diameter the SIFT feature point diameter." << std::endl);
	MDS_CERR("	  -Argument is a double precision number greater than 0.0" << std::endl);
	MDS_CERR("	  -Default value = 30.0" << std::endl);
	MDS_CERR("  -filename Specifies the output file name with features." << std::endl);
	MDS_CERR("	  -Each row in format: ID_vertex and 128x feature vector." << std::endl);
	MDS_CERR("	  -Default value = out.txt" << std::endl);
    MDS_CERR(std::endl);
}

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

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

    // Console application finished
    return 0;
}

