//==============================================================================
/*! \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/20                          \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:
 * - Class filtering informations saved in vertex projection matrix
 */

#ifndef _OM_FILTER_H_
#define _OM_FILTER_H_

#include <OpenMesh\Core\Utils\Property.hh>
#include <fstream>

namespace OMToolkit
{
	/**
	 * Class filtering informations saved in vertex projection matrix
	 * @tparam Mesh Mesh type to work with
	 * @tparam MatrixT Type of vertex matrix property
	 */
	template <class Mesh, class MatrixT>
	class OMFilter
	{
		protected:
			/**
			 * Scalar type in matrices
			 */
			typedef typename MatrixT::Scalar Scalar;

		public:
			/**
			 * Constructor - initializes vital variables and makes a liaison with target mesh
			 * @param mesh Mesh to work with
			 */
			OMFilter(Mesh *mesh);

			/**
			 * Function loads a file with saved filter matrix
			 * File can contain multiple matrices - all are loaded
			 * Structure of a file is following:
			 *
			 * Matrix 2 2
			 * 3 4
			 * 1 2
			 *
			 * First row contains keyword Matrix with specified dimensions, the other rows contains values
			 * After this, another header can follow with second matrix
			 * @param filename File name 
			 * @return True, if loaded successfully
			 */
			bool LoadFile(std::string filename);

			/** 
			 * Function saves internal matrices into file
			 * @param filename File name
			 * @return True, if saved successfully
			 */
			bool SaveFile(std::string filename);

			/**
			 * Filters matrices attached to each vertex and returns value in the middle
			 * Provides only fast filtering - computes only value in the middle of a matrix
			 * Vertex matrix must have the same size asi filter kernel
			 * Kernel must be loaded from file before calling this function
			 * Result is saved into attribute vector (push back)
			 * If there is multiple filtering kernels, result is computed as eucleidian norm of result vector
			 * (result = sqrt(x^2 + y^2 + ... + z^2))
			 * @param matrixHandle Handle to a matrix - universal property handle
			 * @return True if all computation completed successfully
			 */
			bool FilterAll(const OpenMesh::VPropHandleT<MatrixT> &matrixHandle);

			/**
			 * Multiplies each element of the matrix with corresponding weight in filter matrix
			 * Vertex matrix must have the same size asi filter kernel
			 * Kernel must be loaded from file before calling this function
			 * If there is multiple filtering kernels, weighting is computed in series
			 * @param matrixHandle Handle to a matrix - universal property handle
			 * @return True if all computation completed successfully
			 */
			bool WeightAll(const OpenMesh::VPropHandleT<MatrixT> &matrixHandle);

			/**
			 * Saves mean value of attached vertex matrix into attribute vector 
			 * Save is done by push back
			 * @param matrixHandle Handle to a matrix - universal property handle
			 * @return True if all computation completed successfully
			 */
			bool MeanAll(const OpenMesh::VPropHandleT<MatrixT> &matrixHandle);

			/**
			 * Saves median value of attached vertex matrix into attribute vector 
			 * Save is done by push back
			 * @param matrixHandle Handle to a matrix - universal property handle
			 * @return True if all computation completed successfully
			 */
			bool MedianAll(const OpenMesh::VPropHandleT<MatrixT> &matrixHandle);

			/**
			 * Saves minimal value of attached vertex matrix into attribute vector 
			 * Save is done by push back
			 * @param matrixHandle Handle to a matrix - universal property handle
			 * @return True if all computation completed successfully
			 */
			bool MinAll(const OpenMesh::VPropHandleT<MatrixT> &matrixHandle);

			/**
			 * Saves maximal value of attached vertex matrix into attribute vector 
			 * Save is done by push back
			 * @param matrixHandle Handle to a matrix - universal property handle
			 * @return True if all computation completed successfully
			 */
			bool MaxAll(const OpenMesh::VPropHandleT<MatrixT> &matrixHandle);

			/**
			 * Saves a max distance of attached vertex matrix from a plane into attribute vector 
			 * Save is done by push back
			 * @param matrixHandle Handle to a matrix - universal property handle
			 * @return True if all computation completed successfully
			 */
			bool AbsAll(const OpenMesh::VPropHandleT<MatrixT> &matrixHandle);

			/**
			 * Saves a variance of attached vertex matrix from a plane into attribute vector 
			 * Save is done by push back
			 * @param matrixHandle Handle to a matrix - universal property handle
			 * @return True if all computation completed successfully
			 */
			bool VarianceAll(const OpenMesh::VPropHandleT<MatrixT> &matrixHandle);

		private:

			/**
			 * Helper function which computes fast filtering of one matrix (used in FilterAll)
			 * @param vertex Vertex on which we are doing computations
			 * @param matrixHandle Handle to a matrix - universal property handle
			 * @return value in the middle of matrix
			 */
			inline Scalar filterOne(typename Mesh::VertexHandle &vertex, const OpenMesh::VPropHandleT<MatrixT> &matrixHandle);

			/**
			 * Helper function which computes fast filtering of one matrix (smaller filter kernel than tangent matrix)
			 * @param vertex Vertex on which we are doing computations
			 * @param matrixHandle Handle to a matrix - universal property handle
			 */
			inline void filterOneSmall(typename Mesh::VertexHandle &vertex, const OpenMesh::VPropHandleT<MatrixT> &matrixHandle);

			/**
			 * Helper function which multiplies each matrix element with corresponding weight saved in filter matrix
			 * @param vertex Vertex on which we are doing computations
			 * @param matrixHandle Handle to a matrix - universal property handle
			 */
			inline void weightOne(typename Mesh::VertexHandle &vertex, const OpenMesh::VPropHandleT<MatrixT> &matrixHandle);


			/**
			 * Vector of filter kernels
			 */
			std::vector<MatrixT> m_filterKernel;

			/**
			 * Number of kernels for fast access
			 */
			unsigned int m_numKernels;

			/**
			 * Mesh to work with
			 */
			Mesh *m_mesh;

			/**
			 * Is filtration kernel loaded?
			 */
			bool m_loaded;
	};

	#include <OMToolkit\OMFilter.hxx>
} // namespace OMToolkit

#endif