//==============================================================================
/*! \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 "viewer.h"

#include <OMToolkit\IO\OMIO.h>
#include <OMToolkitOSG\OMLegendGeo.h>
#include <OMToolkit\OMTypes.h>
#include <OMToolkitOSG\OMGeometry.h>
#include <OMToolkitOSG\OMDirectionsGeo.h>

#include <osg\geode>
#include <osg\material>
#include <osg\blendfunc>
#include <osgViewer\viewer>
#include <locale>

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

// Module description
const std::string MODULE_DESCRIPTION    = "Module for viewing a mesh";

// Additional command line arguments
const std::string MODULE_ARGUMENTS      = "visualise:component:omit:directions";

// Additional arguments
const std::string MODULE_ARG_VISUALISE	= "visualise";
const std::string MODULE_ARG_NORMALS	= "directions";
const std::string MODULE_ARG_OMMIT		= "omit";
const std::string MODULE_ARG_VECTOR		= "component";

// Visualisation types
const std::string VISUALISE_NONE		= "none";
const std::string VISUALISE_CURVATURE	= "curvature";
const std::string VISUALISE_VATTRIBUTES = "vattributes";
const std::string VISUALISE_VCOLOR		= "vcolor";
const std::string VISUALISE_DEFAULT     = VISUALISE_NONE;

const double VISUALISE_OMMIT_DEFAULT	= 2.0;

// Directions possibilities
const std::string DIRECTIONS_NONE		= "none";
const std::string DIRECTIONS_CURVATURE	= "curvature";
const std::string DIRECTIONS_FNORMALS	= "fnormals";
const std::string DIRECTIONS_VNORMALS	= "vnormals";
const std::string DIRECTIONS_DEFAULT     = DIRECTIONS_NONE;

const double DIRECTION_DEFAULT_LENGHT	= 0.5;
const int VECTOR_COMPONENT_DEFAULT		= 0;
// Mesh type
typedef OMToolkit::Types::ModuleMeshd			MeshT;

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

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

///////////////////////////////////////////////////////////////////////////////////////////////////
// Do on startup
///////////////////////////////////////////////////////////////////////////////////////////////////
bool OMViewer::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_visualisation = VISUALISE_DEFAULT;
	m_Arguments.value(MODULE_ARG_VISUALISE, m_visualisation);
	
	m_vector_component = VECTOR_COMPONENT_DEFAULT;
	if (m_visualisation == VISUALISE_VATTRIBUTES)
	{
		m_Arguments.value(MODULE_ARG_VECTOR, m_vector_component);
		if (m_vector_component < 0)
		{
			MDS_CERR('<' << m_sFilename << "> Vector component cannot be lesser than 0." << std::endl);
			return false;
		}
	}
	m_ommit = VISUALISE_OMMIT_DEFAULT;
	m_Arguments.value(MODULE_ARG_OMMIT, m_ommit);
	if (m_ommit < 0.0 || m_ommit > 100.0)
	{
		MDS_CERR('<' << m_sFilename << "> Ommit must be interval [0..100]" << std::endl);
        return false;
	}

	m_directions = DIRECTIONS_DEFAULT;
	m_Arguments.value(MODULE_ARG_NORMALS, m_directions);

	m_normal_lenght = DIRECTION_DEFAULT_LENGHT;
    // O.K.
    return true;
}

///////////////////////////////////////////////////////////////////////////////////////////////////
// Main module loop
///////////////////////////////////////////////////////////////////////////////////////////////////
bool OMViewer::main()
{
	// I/O channels
    mds::mod::CChannel *pIChannel = getInput(0);

    // 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;
		}
		
		// Create osg hierarchy
		osg::ref_ptr<osg::Group> root = new osg::Group();
		osg::ref_ptr<osg::Geode> geode = new osg::Geode();

		// Analytical curvature computation (sombrero)
		//for (MeshT::VertexIter vertex = mesh.vertices_begin(); vertex != mesh.vertices_end(); ++vertex)
		//{
		//	MeshT::Point point = mesh.point(vertex);
		//	// Gaussova krivost
		//	//double K = -(36*(point[1]*point[1] - point[0]*point[0]));
		//	//double denom = 9*(point[1]*point[1]*point[1]*point[1] + point[0]*point[0]*point[0]*point[0]) + 1 + 18*point[0]*point[0]*point[1]*point[1];
		//	//denom *= denom;
		//	//K /= denom;
		//	double x = point[0];
		//	double y = point[1];

		//	y = -y;
		//	/*double denom = sqrt(9*(x*x*x*x+y*y*y*y) + 1 + 18*x*x*y*y);
		//	double G = 1 + 36 * x*x*y*y;
		//	double L = (6*x) / denom;
		//	double M = (-6*y) / denom;
		//	double N = (-6*x) / denom;
		//	double E = 1 + (3*x*x - 3*y*y)*(3*x*x - 3*y*y);
		//	double F = 18*x*y*(y*y-x*x);*/
		//	
		//	double denom = 1 + 4*sin(x*x+y*y)*sin(x*x+y*y)*(x*x+y*y);
		//	double E = 1 + 4*sin(x*x+y*y)*sin(x*x+y*y)*x*x;
		//	double G = 1 + 4*sin(x*x+y*y)*sin(x*x+y*y)*y*y;
		//	double F = 4*sin(x*x+y*y)*sin(x*x+y*y)*x*y;

		//	double L = (-4*cos(x*x+y*y)*x*x - 2*sin(x*x+y*y)) / denom;
		//	double M = (-4*cos(x*x+y*y)*x*y) / denom;
		//	double N = (-4*cos(x*x+y*y)*y*y - 2*sin(x*x+y*y)) / denom;

		//	
		//	double H = (G*L + E*N - 2*F*M)/(2*(E*G - F*F));
		//	double K = (L*N - M*M)/(E*G - F*F);
		//	double k1 = H - sqrt(H*H - K);
		//	double k2 = H + sqrt(H*H - K);
		//	if (k1 < k2)
		//	{
		//		double aux = k1;
		//		k1 = k2;
		//		k2 = aux;
		//	}

		//	
		//	OMToolkit::Types::OMSerializableVector<float> v;
		//	v.push_back(K);
		//	v.push_back(-H);
		//	v.push_back(k1);
		//	v.push_back(k2);
		//	//v.push_back((k1+k2)/2);
		//	mesh.getAttributes(vertex) = v;
		//}

		if (m_visualisation == VISUALISE_VCOLOR)
		{ 
			mesh.release_vertex_colors();
		}
		else
		// decide visualisation method
		if (m_visualisation == VISUALISE_CURVATURE)
		{
			OMToolkit::OMVisualiser<MeshT, MeshT::AttributeScalar> visualiser(&mesh);
			visualiser.ComputeColors(mesh.getCurvatureMagHandle(), m_ommit);

			MeshT::AttributeScalar min, max, mean;
			visualiser.getLegend(min, mean, max);
			osg::ref_ptr<osg::OMLegendGeometry> hudGeo = new osg::OMLegendGeometry(min, max, 600, 800);
			hudGeo->addToGroup(root);
		}
		else if (m_visualisation == VISUALISE_VATTRIBUTES)
		{
			OMToolkit::OMVectorVisualiser<MeshT, Types::ModuleMeshd::VertexAttributeVector> visualiser(&mesh);
			if (!visualiser.ComputeColors(mesh.getVertexAttributeHandle(), (unsigned int)m_vector_component, m_ommit))
			{
				MDS_CERR('<' << m_sFilename << "> Vector component " << m_vector_component << " does not exist." << std::endl);
				return false;
			}
			MeshT::AttributeScalar min, max, mean;
			visualiser.getLegend(min, mean, max);
			osg::ref_ptr<osg::OMLegendGeometry> hudGeo = new osg::OMLegendGeometry(min, max, 600, 800);
			hudGeo->addToGroup(root);
		}
		// case of wrong visualisation parameter
		else if (m_visualisation != VISUALISE_NONE)
		{
			MDS_CERR('<' << m_sFilename << "> Wrong visualise argument" << std::endl);
			return false;
		}

		// if we will visualise directions, we want to look for mean edge lenght
		if (m_directions != DIRECTIONS_NONE)
		{
			MeshT::EdgeIter end = mesh.edges_end();
			m_normal_lenght = 0.0;
			for (MeshT::EdgeIter edge = mesh.edges_begin(); edge != end; ++edge)
				m_normal_lenght += mesh.calc_edge_length(edge);
			m_normal_lenght /= mesh.n_edges()/DIRECTION_DEFAULT_LENGHT;
		}
		
		// decide showing vectors method
		if (m_directions == DIRECTIONS_CURVATURE)
		{
			/*std::ofstream file;
			file.open("curvatures.csv");
			std::locale system_locale("");
			file.imbue(system_locale);
			file << "X;Y;Z;curvature" << std::endl;
			for (MeshT::VertexIter vertex = mesh.vertices_begin(); vertex != mesh.vertices_end(); ++vertex)
			{
				file << mesh.point(vertex)[0] << ";";
				file << mesh.point(vertex)[1] << ";";
				file << mesh.point(vertex)[2] << ";";

				file << mesh.curvatureMagnitude(vertex) << std::endl;
			}
			file.close();*/
			osg::ref_ptr<osg::OMDirectionsGeometry<MeshT, MeshT::Normal>> directions = new osg::OMDirectionsGeometry<MeshT, MeshT::Normal>(mesh, mesh.getCurvatureHandle(), m_normal_lenght);
			geode->addDrawable(directions); 
		}
		else if (m_directions == DIRECTIONS_VNORMALS)
		{
			mesh.request_face_normals();
			mesh.request_vertex_normals();
			mesh.update_normals();
			osg::ref_ptr<osg::OMDirectionsGeometry<MeshT, MeshT::Normal>> directions = new osg::OMDirectionsGeometry<MeshT, MeshT::Normal>(mesh, mesh.vertex_normals_pph(), m_normal_lenght);
			geode->addDrawable(directions); 
		}
		else if (m_directions == DIRECTIONS_FNORMALS)
		{
			mesh.request_face_normals();
			mesh.update_face_normals();
			osg::ref_ptr<osg::OMDirectionsGeometry<MeshT, MeshT::Normal>> directions = new osg::OMDirectionsGeometry<MeshT, MeshT::Normal>(mesh, mesh.face_normals_pph(), m_normal_lenght);
			geode->addDrawable(directions); 
		}
		else if (m_directions != DIRECTIONS_NONE)
		{
			MDS_CERR('<' << m_sFilename << "> Wrong directions argument" << std::endl);
			return false;
		}
		
		//std::ofstream file;
		//file.open("curvatures.csv");
		//std::locale system_locale("");
		//file.imbue(system_locale);
		//for (MeshT::VertexIter vertex = mesh.vertices_begin(); vertex != mesh.vertices_end(); ++vertex)
		//{
		//	int r = mesh.color(vertex)[0];
		//	int g = mesh.color(vertex)[1];
		//	int b = mesh.color(vertex)[2];

		//	g = g << 8;

		//	//file << mesh.getAttributes(vertex)[2] << ";";
		//	//file << mesh.curvatureMagnitude(vertex) << ";";
		//	file << r+g << ";";
		//	file << std::endl;
		//}

		//std::ofstream aaa;
		//aaa.open("sss.txt");
		//float index = 0;
		//float step = 0.0039;
		////for (float r = 0; r < 1; r += step)
		////for (float g = 0; g < 1; g += step)
		//for (float b = 0; b < 1; b += step)
		//{
		//	aaa << b << ";" << 0 << ";";
		//	aaa << b+0.0001 << ";" << 1 << ";";
		//}
		//aaa << std::endl;
		//aaa << std::endl;
		//for (float r = 0; r < 1; r += step)
		//{
		//	aaa << r << ";" << index << ";";
		//	aaa << r+0.0001 << ";" << index+step << ";";
		//	index += step;
		//}

		//aaa.close();

		//file.close();

		// create a model
		osg::ref_ptr<osg::OMGeometry<MeshT>> geometry= new osg::OMGeometry<MeshT>(mesh);
		
		// set two side model
		geometry->getOrCreateStateSet()->setMode(GL_BLEND,osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE);
		osg::Material* mat = new osg::Material;
		mat->setAlpha(osg::Material::FRONT_AND_BACK, .5);
		mat->setColorMode(osg::Material::AMBIENT_AND_DIFFUSE);
		geometry->getOrCreateStateSet()->setAttributeAndModes(mat,osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
		/*geometry->getOrCreateStateSet()->setAttributeAndModes(new osg::BlendFunc(osg::BlendFunc::SRC_ALPHA, osg::BlendFunc::ONE_MINUS_SRC_ALPHA ));
		geometry->getOrCreateStateSet()->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
		geometry->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF);*/
			
		// add all to geode
		geode->addDrawable(geometry); 
		root->addChild(geode);

		// Create a viewer and show it
		osgViewer::Viewer viewer;
		viewer.setUpViewInWindow(50, 50, 800, 600);
		viewer.setSceneData( root );
		MDS_LOG_NOTE("Viewing model: " << mesh.n_vertices() << " vertices.");
		viewer.run();
    }
    else
    {
        MDS_LOG_NOTE("Wait timeout");
    }
    // Returning 'true' means to continue processing the input channel
    return false;
}

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

///////////////////////////////////////////////////////////////////////////////////////////////////
// Writes extended use of this module
///////////////////////////////////////////////////////////////////////////////////////////////////
void OMViewer::writeExtendedUsage(std::ostream& Stream)
{
	MDS_CERR("Extending arguments: [-visualise what] [-component which] [-directions ofWhat]" << std::endl);
    MDS_CERR("Options:" << std::endl);
	MDS_CERR("  -visualise tells, what information shold be shown on model by color scale. Possibilities:" << std::endl);
	MDS_CERR("		none: Light grey color of model" << std::endl);
	MDS_CERR("		curvature: Shows computed curvature (must be computed!)" << std::endl);
	MDS_CERR("		vattributes: Shows computed vertex attributes (must select vector component to show" << std::endl);
	MDS_CERR("		DEFAULT: none" << std::endl);
	MDS_CERR("  -component tells, what component of attribute vector will be visualised (default 0)." << std::endl);
	MDS_CERR("  -directions tells, what vectors should be shown on model" << std::endl);
	MDS_CERR("		none: Shows nothing" << std::endl);
	MDS_CERR("		fnormals: Shows face normals." << std::endl);
	MDS_CERR("		vnormals: Shows vertex normals." << std::endl);
	MDS_CERR("		curvature: Shows vertex curvature (must be computed!)." << std::endl);
	MDS_CERR("		           Attention! There is no curvature directions for Gauss curvature." << std::endl);
	MDS_CERR("		           For mean curvature, mean curvature normal is visualised." << std::endl);
	MDS_CERR("		           For minimum and maximum curvature, main directions are visualised." << std::endl);
	MDS_CERR("		DEFAULT: none" << std::endl);
	MDS_CERR("  -omit: Number of highest/lowest values per cent, which will be omitted from visualize" << std::endl);
	MDS_CERR("		Value [0..100]" << std::endl);
	MDS_CERR("		DEFAULT: 2%" << std::endl);
}

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

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

    // Console application finished
    return 0;
}
