//==============================================================================
/*! \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/08                          \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/>.
 * 
 * Description:
 * - Methods computing a mesh color from specified scalar attributes
 * - Color is set like unsigned char vector (r, g, b, 255)
 */

#ifndef _OM_VISUALISER_HXX_
#define _OM_VISUALISER_HXX_

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Constructor - creates a link to a mesh and initializes variables
// @param mesh Pointer to a mesh
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
template <class Mesh, class Scalar>
OMVisualiser<Mesh, Scalar>::OMVisualiser(Mesh *mesh) : importer(*mesh)
{
	m_mesh = mesh;
	m_maxBlue = 0.0;
	m_maxGreen = 0.0;
	m_maxRed = 0.0;
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Computes colors from a vertex vector property and saves them into mesh
// @param vertexProperty Handle to a property, from which we will compute colors
// @param vectorComponent Number of vector component to be visualised
// @param ommitExtremaPercent Number of percent which will be ommited from extrema (for ex. value 2.0 ommits 2% of brightest and darkest points)
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
template <class Mesh, class Vector>
bool OMVectorVisualiser<Mesh, Vector>::ComputeColors(OpenMesh::VPropHandleT<Vector> vertexProperty, unsigned int vectorComponent, Scalar ommitExtremaPercent)
{
	if (!m_mesh->has_vertex_colors())
		m_mesh->request_vertex_colors();

	Mesh::VertexIter end = m_mesh->vertices_end();

	std::vector<Scalar> sorted;
	
	// sort all values
	for (Mesh::VertexIter vertex = m_mesh->vertices_begin(); vertex != end; ++vertex)		
	{
		if (m_mesh->property(vertexProperty, vertex).size() <= vectorComponent)
			return false;
		sorted.push_back(m_mesh->property(vertexProperty, vertex)[vectorComponent]);
	}
	std::sort(sorted.begin(), sorted.end());

	Scalar percent = 1.0;
	if (ommitExtremaPercent > 0)
	{
		percent = 100 / ommitExtremaPercent;
		m_maxBlue = sorted[sorted.size()/percent];
		m_maxRed = sorted[sorted.size()-sorted.size()/percent];
	}
	else
	{
		m_maxBlue = sorted[0];
		m_maxRed = sorted[sorted.size()];
	}

	/*
	_________            _________
			 \    /\    /
			  \  /  \  /
			   \/    \/
			  /	\    /\ 
			 /	 \  /  \
			/ 	  \/    \
		maxB     maxG    maxR
	*/

	// set minimum, maximum and mean
	m_maxGreen = (m_maxBlue + m_maxRed) / 2;
				
	m_Difference = m_maxBlue;
	m_maxBlue -= m_Difference;
	m_maxRed -=  m_Difference;
	m_maxGreen -= m_Difference;

	for (Mesh::VertexIter vertex = m_mesh->vertices_begin(); vertex != end; ++vertex)		
	{
		importer.set_color(vertex, getColor(m_mesh->property(vertexProperty, vertex)[vectorComponent]));						
	}
	return true;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Computes colors from a vertex scalar property and saves them into mesh
// @param vertexProperty Handle to a property, from which we will compute colors
// @param ommitExtremaPercent Number of percent which will be ommited from extrema (for ex. value 2.0 ommits 2% of brightest and darkest points)
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
template <class Mesh, class Scalar>
void OMVisualiser<Mesh, Scalar>::ComputeColors(OpenMesh::VPropHandleT<Scalar> vertexProperty, Scalar ommitExtremaPercent)
{
	if (!m_mesh->has_vertex_colors())
		m_mesh->request_vertex_colors();

	Mesh::VertexIter end = m_mesh->vertices_end();

	std::vector<Scalar> sorted;

	// sort all values
	for (Mesh::VertexIter vertex = m_mesh->vertices_begin(); vertex != end; ++vertex)		
		sorted.push_back((Scalar)m_mesh->property(vertexProperty, vertex));
	std::sort(sorted.begin(), sorted.end());

	Scalar percent = 1.0;
	if (ommitExtremaPercent > 0)
	{
		percent = 100 / ommitExtremaPercent;
		m_maxBlue = sorted[sorted.size()/percent];
		m_maxRed = sorted[sorted.size()-sorted.size()/percent];
	}
	else
	{
		m_maxBlue = sorted[0];
		m_maxRed = sorted[sorted.size()];
	}

	/*
	_________            _________
			 \    /\    /
			  \  /  \  /
			   \/    \/
			  /	\    /\ 
			 /	 \  /  \
			/ 	  \/    \
		maxB     maxG    maxR
	*/

	// set minimum, maximum and mean
	m_maxGreen = (m_maxBlue + m_maxRed) / 2;
				
	m_Difference = m_maxBlue;
	m_maxBlue -= m_Difference;
	m_maxRed -=  m_Difference;
	m_maxGreen -= m_Difference;

	for (Mesh::VertexIter vertex = m_mesh->vertices_begin(); vertex != end; ++vertex)		
	{
		importer.set_color(vertex, getColor(m_mesh->property(vertexProperty, vertex)));						
	}
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Computes colors from a face scalar property and saves them into mesh
// @param vertexProperty Handle to a property, from which we will compute colors
// @param ommitExtremaPercent Number of percent which will be ommited from extrema (for ex. value 2.0 ommits 2% of brightest and darkest points)
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
template <class Mesh, class Scalar>
void OMVisualiser<Mesh, Scalar>::ComputeColors(OpenMesh::FPropHandleT<Scalar> faceProperty, Scalar ommitExtremaPercent)
{
	if (!m_mesh->has_face_colors())
		m_mesh->request_face_colors();

	Mesh::FaceIter end = m_mesh->faces_end();

	std::vector<Scalar> sorted;

	// sort all values
	for (Mesh::FaceIter face = m_mesh->faces_begin(); face != end; ++face)		
		sorted.push_back((Scalar)m_mesh->property(faceProperty, face));
	std::sort(sorted.begin(), sorted.end());

	Scalar percent = 1.0;
	if (ommitExtremaPercent > 0)
	{
		percent = 100 / ommitExtremaPercent;
		m_maxBlue = sorted[sorted.size()/percent];
		m_maxRed = sorted[sorted.size()-sorted.size()/percent];
	}
	else
	{
		m_maxBlue = sorted[0];
		m_maxRed = sorted[sorted.size()];
	}
	/*
	_________            _________
			 \    /\    /
			  \  /  \  /
			   \/    \/
			  /	\    /\ 
			 /	 \  /  \
			/ 	  \/    \
		maxB     maxG    maxR
	*/

	// set minimum, maximum and mean
	
	m_maxGreen = (m_maxBlue + m_maxRed) / 2;
		
	m_Difference = m_maxBlue;
	m_maxBlue -= m_Difference;
	m_maxRed -=  m_Difference;
	m_maxGreen -= m_Difference;

	for (Mesh::FaceIter face = m_mesh->faces_begin(); face != end; ++face)		
	{
		importer.set_color(face, getColor(m_mesh->property(faceProperty, face)));						
	}
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Method computes a color from given value
// @param current Property value
// @return Color vector
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
template <class Mesh, class Scalar>
OpenMesh::Vec4uc OMVisualiser<Mesh, Scalar>::getColor(Scalar current)
{
	current -= m_Difference;

	Scalar r = 0.0;
	Scalar g = 0.0;
	Scalar b = 0.0;

	// blue and green
	if (current > m_maxGreen)
	{
		r = (255.0/(m_maxRed-m_maxGreen)) * current - ((255.0 / (m_maxRed-m_maxGreen))*m_maxRed) + 255.0;
		g = (-255.0 / (m_maxRed-m_maxGreen)) * current + ((255.0 / (m_maxRed-m_maxGreen))*m_maxRed);
	}
	// red and green
	else
	{
		g = (255.0/(m_maxGreen-m_maxBlue)) * current - ((255.0 / (m_maxGreen-m_maxBlue))*m_maxGreen) + 255.0;
		b = (-255.0 / (m_maxGreen-m_maxBlue)) * current +((255.0 / (m_maxGreen-m_maxBlue))*m_maxGreen);
	}
	
	/*std::ofstream file;
	file.open("output.csv", std::ios_base::app);
	if (m_maxBlue > current) file << m_maxBlue << std::endl;
	else if (m_maxRed < current) file << m_maxRed << std::endl;
	else file << current << std::endl;
	file.close();*/

	// control due to ommiting some values
	if (r < 0) r = 0;
	if (g < 0) g = 0;
	if (b < 0) b = 0;
	if (r > 255) r = 255;
	if (g > 255) g = 255;
	if (b > 255) b = 255;
							
	return OpenMesh::Vec4uc((unsigned char)r, (unsigned char)g, (unsigned char)b, 255);
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Returns values of Red, Green and Blue extremas (for painting a legend)
// @param maxBlue Property value for (0, 0, 255, 255) color
// @param maxGreen Property value for (0, 255, 0, 255) color
// @param maxRed Property value for (255, 0, 0, 255) color
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
template <class Mesh, class Scalar>
void OMVisualiser<Mesh, Scalar>::getLegend(Scalar &maxBlue, Scalar &maxGreen, Scalar &maxRed)
{
	maxBlue = m_maxBlue + m_Difference;
	maxGreen = m_maxGreen + m_Difference;
	maxRed = m_maxRed + m_Difference;
}

#endif