//============================================================================
//
// This file is part of the Shapes Generative Model Library (SGML) project.
//
// Copyright (c) 2010-2014 - Evangelos Kalogerakis, Siddhartha Chaudhuri (authors of the code) / Stanford University, UMass-Amherst
//
// SGML is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// SGML 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 General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with SGML.  If not, see <http://www.gnu.org/licenses/>.
//
//============================================================================

#ifndef __DEF_MESHCREATOR_H
#define __DEF_MESHCREATOR_H

#include "S12_ShapeBNet.hpp"
#include "S12_Common.hpp"

#define  MAX_NUM_SUBLABELS		100

namespace SNL {
namespace S12 {


struct SNL_S12_API SampledPart
{
public:
	size_t                  id;
	MeshPart				        *ref_part;
	MeshPart				        *adj_ref_part_that_sent_transformation;
	TriMesh*								ref_mesh;
	vector<double>          scale_data; 
	pair<vec, point>        transformation_with_respect_to_reference_part;
	vector< SampledPart* >  adj_parts;
	vector< int >           should_be_attached_to_boundary_id_of_ref_part;
	
	SampledPart(size_t _id, MeshPart *_ref_part, vector<double>& _scale_data): id(_id), ref_part(_ref_part), scale_data(_scale_data), ref_mesh(NULL), adj_ref_part_that_sent_transformation(NULL) {};
};


class SNL_S12_API MeshCreator
{
private:
	size_t												 id;
	vector<Label*>								 labels;        // original labels
	TriMesh												 newmesh;

	void	validateSampledParts( vector<MeshPart*>& parts, vector<TriMesh*>& meshes);
	inline int getSampledPart(size_t id)
	{
		for (size_t i = 0; i < sampled_parts.size(); i++)
			if ( sampled_parts[i].id == id )
				return i;
		return -1;
	}
public:
	vector< SampledPart >          sampled_parts;
	vector< MeshPart* >  best_parts;
	bool valid_sampled_mesh;
	MeshPart *problematic_part;
	MeshPart *getRefPart(MeshPart*, vector<MeshPart*>& );
	MeshCreator() {};

	MeshCreator( ShapeBNetSample& sample,  vector<Label*>& labels, vector<MeshPart*>& parts, vector<TriMesh*>& meshes, vector< vector< MeshPart* > >& all_samples_best_parts, const char* part_path, size_t id );
	//MeshCreator( ShapeBNetSample& sample,  vector<Label*>& labels, vector<MeshPart*>& parts, vector<TriMesh*>& meshes, const char* part_path, size_t id, bool strict = false );
	//MeshCreator( ShapeBNetSample& sample,  vector<Label*>& labels, vector<MeshPart*>& parts, vector<TriMesh*>& meshes, const char* part_path, size_t id, bool strict, string use_old_id );
	void exportObj( ShapeBNetSample& sample, char* output_path);
	void exportObjCV( ShapeBNetSample& sample, char* output_path);
	ShapeBNetSample* importObj(char* samplefilename, vector<Label*>& labels, vector<MeshPart*>& parts, vector<TriMesh*>& meshes);
	~MeshCreator();
};






// helper function
template <class T>
inline void add_vote( std::vector< std::pair<T, size_t> >& votes, T& o)
{
	for (size_t i = 0; i < votes.size(); i++)
	{
		if ( votes[i].first == o )
		{
			votes[i].second++;
			return;
		}
	}
	votes.push_back( std::pair<T, size_t>(o, 1) );
}

template <class T>
inline vector<T> order_elements_according_to_votes( vector< pair<T, size_t> > votes)
{
	vector<T> ordered_elements;
	for (size_t i = 0; i < votes.size(); i++)
	{
		size_t max_num_votes = 0;
		size_t highest_voted_elem_pos;
		for (size_t j = 0; j < votes.size(); j++)
		{
			if ( votes[j].second > max_num_votes )
			{
					max_num_votes = votes[j].second;
					highest_voted_elem_pos = j;
			}
		}
		votes[ highest_voted_elem_pos ].second = 0;
		ordered_elements.push_back( votes[ highest_voted_elem_pos ].first );
	}
	return ordered_elements;
}

template <class T>
static inline std::ostream &operator << (std::ostream &os, const vector< pair<T, size_t> >& v)
{
	os << v.size() << " ";
	for (size_t i = 0; i < v.size(); i++)
		os << "(" << v[i].first << ",#votes=" << v[i].second << ") ";
	return os;
}



static bool votesTest() {
	std::cout << "Testing add_vote/order_elements_according_to_votes functionality...";

	vector< pair<string, size_t> > votes;
	add_vote( votes, string("malakia3"));
	add_vote( votes, string("malakia3"));
	add_vote( votes, string("malakia3"));
	add_vote( votes, string("malakia2"));
	add_vote( votes, string("malakia2"));
	add_vote( votes, string("malakia1"));
	add_vote( votes, string("malakia4"));
	add_vote( votes, string("malakia4"));
	add_vote( votes, string("malakia4"));
	add_vote( votes, string("malakia4"));

	vector<string> my_strings = order_elements_according_to_votes( votes );
	if ( my_strings[0] == "malakia4" && my_strings[1] == "malakia3" && my_strings[2] == "malakia2" && my_strings[3] == "malakia1" )
	{
		std::cout << "Pass!" << std::endl;
		return true;
	}
	else
	{
		std::cout << "Fail." << std::endl;
		return false;
	}
}

}
}

#endif
