#include "point_set.h"
#include <gl/gl.h>
#include "vertex_group.h"
#include "color_table.h"
#include "../basic/stop_watch.h"



#ifndef GL_MULTISAMPLE
#define GL_MULTISAMPLE 0x809D
#endif


PointSet::PointSet(SceneGraph* sg) 
: ActiveObject(sg)
, vertices_(nil)
, nb_vrtices_(0)
, bbox_exist_(false)
, normals_exist_(false)
, curvature_exist_(false)
{
}


const Bbox3f& PointSet::bbox() const {
	if (bbox_exist_)
		return bbox_;

	std::vector<Point3f> pts;
	int step = 1;

	for (int i=0; i<nb_vrtices_; i+=step) {
		const Vertex* v = vertices_[i];
		pts.push_back(v->point());
	}

	bbox_ = Global::bbox_of(pts);
	bbox_exist_ = true;
	return bbox_;
}


void PointSet::draw(bool draw_normals, int normal_skip /* = 5*/, bool fast /* = false */, int max_num /* = 200000 */) {
	//glDisable(GL_LIGHTING);
	glEnable(GL_LIGHTING);
	glPointSize(Global::point_size);

	glBegin(GL_POINTS);
	if (fast && nb_vrtices_ > max_num) {
		int partial = nb_vrtices_ / max_num;
		for (int i=0; i<nb_vrtices_; i+=partial) {
			const Vertex* v = vertices_[i];
			if (v->is_visible()) {
				const Vector3f& n = v->normal();
				const Point3f&  p = v->point();
				glColor4fv(vertices_[i]->color().data());
				glNormal3f(n.x(), n.y(), n.z());
				glVertex3f(p.x(), p.y(), p.z());
			}
		}
	} else {
		for (int i=0; i<nb_vrtices_; ++i) {
			const Vertex* v = vertices_[i];
			if (v->is_visible()) {
				const Vector3f& n = v->normal();
				const Point3f&  p = v->point();
				glColor4fv(vertices_[i]->color().data());
				glNormal3f(n.x(), n.y(), n.z());
				glVertex3f(p.x(), p.y(), p.z());
			}
		}
	}
	glEnd();

	if (draw_normals) {	
		Bbox3f box = bbox();
		float  len = ((box.xmax() - box.xmin()) + (box.ymax() - box.ymin()) + (box.zmax() - box.zmin())) / 3.0f;
		len *= 0.08f;
		glDisable(GL_LIGHTING);
		glLineWidth(1.0f);
		glBegin(GL_LINES);
		for (int i = 0; i < nb_vrtices_; i += normal_skip) {
			const Vertex* v = vertices_[i];
			if (v->is_visible()) {
				const Vector3f& n = v->normal() * len;
				const Point3f&  p = v->point();

				glColor3f(1.0f, 0.0f, 0.0f);
				glVertex3f(p.x(), p.y(), p.z());
				glVertex3f(p.x() + n.x(), p.y() + n.y(), p.z() + n.z());
			}
		}
		glEnd();
	}

	glEnable(GL_LIGHTING);
}


void PointSet::clear() {
	clear_vertex_groups();

	if (vertices_ != nil) {
		for (int i=0; i<nb_vrtices_; ++i) 
			delete vertices_[i];

		delete [] vertices_;
		vertices_ = nil;
	}

	for (unsigned int i = 0; i < vertex_groups_.size(); i++)
		delete vertex_groups_[i];

	bbox_exist_ = false;
}

void PointSet::changed() {
	bbox_exist_ = false;
	update_canvas();
}

void PointSet::assign_color_by_normal() {
	for (int i=0; i<nb_vrtices_; ++i) {
		Vertex* v = vertices_[i];
		const Vector3f& n = v->normal(); 
		int r = int(std::min(std::max(128.0 * (n.x() + 1.0), 0.0), 255.99));
		int g = int(std::min(std::max(128.0 * (n.y() + 1.0), 0.0), 255.99));
		int b = int(std::min(std::max(128.0 * (n.z() + 1.0), 0.0), 255.99));
		v->set_color(Colorf(r/256.0f, g/256.0f, b/256.0f));
	}
}


void PointSet::assign_color_by_height() {
	// if bbox doesn't exist, compute first
	const Bbox3f& box = bbox();

	float factor = 256.0f / (box.zmax() - box.zmin());
	for (int i=0; i<nb_vrtices_; ++i) {
		Vertex* v = vertices_[i];
		int index = static_cast<int>((v->point().z() - box.zmin()) * factor);

		const Colorf& color = Global::color_from_table(index);
		v->set_color(color);
	}
}


void PointSet::clear_vertex_groups() { 
	for (int i=0; i<nb_vrtices_; ++i) {
		Vertex* v = vertices_[i];
		v->set_vertex_group(nil);
	}	
	
	for (unsigned int i=0; i<vertex_groups_.size(); ++i)
		delete vertex_groups_[i];
	vertex_groups_.clear(); 
}

void PointSet::add_vertex_group(VertexGroup* g) { 
	std::vector<VertexGroup*>::iterator pos = std::find(vertex_groups_.begin(), vertex_groups_.end(), g);
	if (pos != vertex_groups_.end()) {
		Logger::output(title()) << "vertex group already exist." << Logger::endl();
		return;
	}
	
	g->set_point_set(this);
	g->unify();  // always do this is stupid. But...

	vertex_groups_.push_back(g);

	for (unsigned int i=0; i<g->size(); ++i) {
		Vertex* v = g->at(i);
		v->set_vertex_group(g);
	}
}


void PointSet::delete_vertex_group(VertexGroup* g) { 
	std::vector<VertexGroup*>::iterator pos = std::find(vertex_groups_.begin(), vertex_groups_.end(), g);
	if (pos != vertex_groups_.end()) {
		vertex_groups_.erase(pos);	

		delete g;
	}
}

void PointSet::remove_vertex_group(VertexGroup* g) { 
	std::vector<VertexGroup*>::iterator pos = std::find(vertex_groups_.begin(), vertex_groups_.end(), g);
	if (pos != vertex_groups_.end()) {
		vertex_groups_.erase(pos);
	}
}


VertexGroup* PointSet::merge_some_vertex_groups(const std::vector<VertexGroup*>& groups) {
	float total_points = 0;
	VertexGroup* obj = new VertexGroup;
	
	float r, g, b, a;
	r = g = b = a = 0;

	for (unsigned int i=0; i<groups.size(); ++i) {
		VertexGroup* grp = groups[i];
		r += grp->color().r() * grp->size();
		g += grp->color().g() * grp->size();
		b += grp->color().b() * grp->size();
		total_points += grp->size();
	}

	Colorf new_color(r /= total_points, g /= total_points, b /= total_points, 1.0f);

	for (unsigned int i=0; i<groups.size(); ++i) {
		VertexGroup* grp = groups[i];
		obj->insert(obj->end(), grp->begin(), grp->end());

		obj->add_child(grp);
		grp->set_parent(obj);

		remove_vertex_group(grp);
	}

	obj->set_color(new_color);
	obj->set_point_set(this);
	vertex_groups_.insert(vertex_groups_.begin(), obj); // put it in the front.

	obj->unify();  // all do this is stupid. But...
	return obj;
}


void PointSet::break_some_parent_groups(const std::vector<VertexGroup*>& groups) {
	std::vector<VertexGroup*> parentGroups;
	for (unsigned int i=0; i<groups.size(); ++i) {
		VertexGroup* g = groups[i];
		if (g->children().size() > 1) {
			parentGroups.push_back(g);
		}
	}

	if (groups.empty()) {
		Logger::output(title()) << "Warning: no group containing children." << Logger::endl();
		return;
	} 

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

	for (unsigned int i=0; i<parentGroups.size(); ++i) {
		VertexGroup* g = parentGroups[i];
		std::vector<VertexGroup*> children = g->children();
		for (unsigned int j=0; j<children.size(); ++j) {
			VertexGroup* chld = children[j];
			chld->set_color(chld->color());
			chld->set_parent(nil);
			add_vertex_group(chld);
		}
		remove_vertex_group(g);
	}

	Logger::output(title()) << parentGroups.size() << " groups are broken into single groups." << Logger::endl();

}


PointSet* PointSet::copy() const {
	PointSet* point_set = new PointSet;

	Vertex** vts = new Vertex*[nb_vrtices_];
	for (int i=0; i<nb_vrtices_; ++i) {
		const Point3f& p = vertices_[i]->point();
		const Vector3f& n = vertices_[i]->normal();
		vts[i] = new Vertex(p.x(), p.y(), p.z(), n.x(), n.y(), n.z());
 
	}

	point_set->set_vertices(vts, nb_vrtices_);
	return point_set;
}

void PointSet::set_vertices(Vertex** pts, int num) {
	vertices_ = pts; 
	nb_vrtices_ = num;

	for (int i=0; i<num; ++i) {
		vertices_[i]->set_index(i);
	}	
}