#pragma once

/********************************************************************
created:	 2009/07/23
created:	 23:7:2009   19:21
file base:	 vertex_group.h
author:		 Liangliang Nan
contact:     liangliang.nan@gmail.com
affiliation: Shenzhen Institute of Advanced Technology

purpose:	 
*********************************************************************/


#include "vertex.h"
#include "geometry_global.h"
#include "../widget/logger.h"

#include <vector>



class PointSet;

class VertexGroup : public std::vector<Vertex*>, public SelectableItem
{
public:
	VertexGroup() 
		: point_set_(nil)
		, centroid_valid_(false)
		, bbox_valid_(false)
		, label_("unknown")
		, confidence_(0.0f)
		, parent_(nil)
	{}

	~VertexGroup() { delete_children(); this->clear(); /*delete std::vector<Vertex*>*;*/}

	std::string title() const { return "[VertexGroup]: "; }

	PointSet* point_set() { return point_set_; }
	void set_point_set(PointSet* ps) { point_set_ = ps; }

	unsigned int nb_vertice() { return size(); }

	virtual void set_color(const Colorf& c) ;

	void set_visible(bool b);

	//////////////////////////////////////////////////////////////////////////

	const std::string& label() const { return label_; }
	void set_label(const std::string& lb) { label_ = lb; } 

	float confidence() const { return confidence_; }
	void set_confidence(float conf) { confidence_ = conf; } 
	
	//////////////////////////////////////////////////////////////////////////

	VertexGroup* parent() { return parent_; }
	void set_parent(VertexGroup* g) { parent_ = g; }

	std::vector<VertexGroup*> children() ;
	void set_children(const std::vector<VertexGroup*>& chld) ;

	void add_child(VertexGroup* g) ;
	void remove_child(VertexGroup* g) ;

	void remove_children();
	void delete_children();

	//////////////////////////////////////////////////////////////////////////

	void compute_bbox();
	Bbox3f bbox() const;

	void changed() { 
		bbox_valid_ = false;
		centroid_valid_ = false;
	}

	Point3f centroid() const ;
	void update_centroid() ;

	void draw() const {} // no use
	virtual void draw_with_name(unsigned int i) const ;

	void unify() ;
	void sort() { std::sort(begin(), end()); }

private:
	PointSet*				point_set_;

	std::string				label_;
	float					confidence_;

	VertexGroup*			parent_;
	std::set<VertexGroup*>	children_;

	//////////////////////////////////////////////////////////////////////////
	
	mutable	Bbox3f	bbox_;
	mutable	bool	bbox_valid_;

	mutable	Point3f	centroid_;
	mutable	bool	centroid_valid_;
};




class VertexGroupCmp
{
public:
	VertexGroupCmp() {}

	bool operator()(const VertexGroup* g0, const VertexGroup* g1) const {
		return g0->size() > g1->size();
	}

};


inline Point3f VertexGroup::centroid() const { 
	if (centroid_valid_ == false) 
		(const_cast<VertexGroup*>(this))->update_centroid();
	
	return centroid_; 
}

inline void VertexGroup::update_centroid() {
	Vector3f c(0.0f, 0.0f, 0.0f);
	for (unsigned int j=0; j<size(); ++j) {
		c = c + (at(j)->point() - CGAL::ORIGIN);
	}
	centroid_ = CGAL::ORIGIN + c / float(size());
	centroid_valid_ = true;
} 

inline void VertexGroup::unify() {
	std::sort(begin(), end());
	std::unique(begin(), end());
}

inline void VertexGroup::compute_bbox() {
	bbox_ = Global::bbox_of(*this);
	bbox_valid_ = true;
}
	

//////////////////////////////////////////////////////////////////////////
	
inline void VertexGroup::set_color(const Colorf& c) { 
	SelectableItem::set_color(c);

	for (unsigned int j=0; j<size(); ++j) {
		at(j)->set_color(c);
	}

	for (std::set<VertexGroup*>::iterator it=children_.begin(); it!=children_.end(); ++it) {
		(*it)->set_color(c);
	}	
}


inline void VertexGroup::set_visible(bool b) {
	SelectableItem::set_visible(b);

	for (unsigned int j=0; j<size(); ++j) {
		at(j)->set_visible(b);
	} 
}


inline std::vector<VertexGroup*> VertexGroup::children() {
	std::vector<VertexGroup*> tmp(children_.begin(), children_.end());
	return tmp;
}

inline void VertexGroup::set_children(const std::vector<VertexGroup*>& chld) {
	children_.clear();
	children_.insert(chld.begin(), chld.end());

	for (unsigned int i=0; i<chld.size(); ++i)	{
		chld[i]->set_point_set(this->point_set());
		chld[i]->set_color(color());
	}
}

inline void VertexGroup::add_child(VertexGroup* g) {
	g->set_point_set(this->point_set());
	children_.insert(g);

	g->set_color(color());
	unify();
}

inline void VertexGroup::remove_child(VertexGroup* g) {
	children_.erase(g);
}

inline void VertexGroup::remove_children() {
	children_.clear();
}

inline void VertexGroup::delete_children() {
	for (std::set<VertexGroup*>::iterator it=children_.begin(); it!=children_.end(); ++it) {
		delete (*it);
	}
	children_.clear();
}


inline Bbox3f VertexGroup::bbox() const { 
	if (bbox_valid_ == false)
		const_cast<VertexGroup*>(this)->compute_bbox();
	return bbox_;
}


inline void VertexGroup::draw_with_name(unsigned int i) const {
	glPushName(i);
	for (unsigned int j=0; j<size(); ++j) {
		const Point3f& p = at(j)->point();
		glRasterPos3f(p.x(), p.y(), p.z());
	}
	glPopName();
}
