/* -*- mia-c++ -*-
 *
 * This file is part of viewitgui - a library and program for the
 * visualization of 3D data sets. 
 *
 * Copyright (c) Leipzig, Madrid 1999-2013 Mirco Hellmann, Gert Wollny
 *
 * viewitgui 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.
 *
 * viewitgui 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 viewitgui; if not, see <http://www.gnu.org/licenses/>.
 */


#ifndef d__mesh_hh
#define d__mesh_hh

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <GL/gl.h>

#include <viewit/drawable.hh>

#include <mia.hh>
using namespace mia; 

#include <memory>
#include <viewit/structures.hh>

struct base_header {
	char name_space[3];
	char type[14];
	short endian_test;
};

struct tri_mesh_header {
	base_header base; 
	size_t n_vertices; 
	size_t n_triangles; 
};	
	

#define TRIMESH_TYPENAME "TriMesh"
#define SMOOTHTRIMESH_TYPENAME "STriMesh"
#define COLORSMOOTHTRIMESH_TYPENAME "CSTriMesh"
#define TRIANGLEMESHABASE_CLASSNAME "CTriangleMeshABase"
#define TRIANGLEMESH_CLASSNAME "CTriangleMeshA"
#define SMOOTHTRIANGLEMESH_CLASSNAME "TSmoothTriangleMesh"
#define COLORSMOOTHMESH_CLASSNAME "TColorSmoothMesh"

/// the base class for all triangle meshes
class CTriangleMeshABase: public TDrawable {
protected:
	size_t n_triangles;
	
	triangle_ui_t *triangles; 
	size_t n_vertices; 
	C3DFVector *triangle_mid_points; 
	mutable C3DFVector m_center; 
	
public:
	CTriangleMeshABase(const string& name, size_t n_triangles, triangle_ui_t *t,size_t _num_vertices);
	CTriangleMeshABase(const string& name, const CTriangleMeshABase& org);
	~CTriangleMeshABase();
	
	virtual const TVertexList *get_vertex_list(int stride)const = 0; 
	
	virtual const char* get_type_name()const = 0; 
	virtual const char* get_classname()const;
	virtual void get_classname_list(list<string> *classlist)const;
	
	const C3DFVector get_center()const; 
	
	bool do_export(FILE*f)const; 
protected:
	triangle_ui_t *tri_mirror;
	
	virtual bool do_handle_key_event(int key);
	virtual void do_gl_draw(const TCamera& c) const;
private:
	class collect_t {
	public:
		int i; 
		float d; 
		bool operator < (const collect_t& o)const {
			return d < o.d; 
		}
	};
	void povray_output_description(FILE *f)const;
	virtual bool write_vertices(FILE *f)const=0; 
	
protected:			
	
	template <class Vertex> 
	bool do_write_vertices(FILE *f)const; 
	
	template <class triangle>
	void sort_triangles(const triangle *in, triangle *out, const TCamera& c)const;
	
	template <class vertex_type>
	void calc_triangle_mid_points(vertex_type *vertex);
	
	template <class vertex_type>
	void calc_center(vertex_type *vertex);
	
};

/// a triangle mesh with no normals (can not be lit)
class CTriangleMeshA: public CTriangleMeshABase {
	vertex_t *vertices;
public:
	CTriangleMeshA(const string& name, int _n_triangles, triangle_ui_t *t,
			    int _num_vertices, vertex_t *v);
	~CTriangleMeshA();
	const vertex_t *get_vertices()const {return vertices;}
	virtual TVertexList *get_vertex_list(int stride)const;

	virtual void povray_output_object(FILE *f)const;
	virtual const char* get_type_name()const;
	virtual const char* get_classname()const;
	virtual void get_classname_list(list<string> *classlist)const;

protected: 	
	virtual bool write_vertices(FILE *f)const; 
	void gl_attach();
	void do_gl_draw(const TCamera& c) const; 
};

/// a trinagle mesh with normals at the vertexes
class TSmoothTriangleMesh: public CTriangleMeshABase {
	normal_vertex_t *vertices; 
public:
	TSmoothTriangleMesh(const string& name, int _n_triangles, triangle_ui_t *t,
			    int _num_vertices, normal_vertex_t *v);
	
	TSmoothTriangleMesh(const string& name, int _n_triangles, triangle_ui_t *t,
			    int _num_vertices, vertex_t *v);
	
	TSmoothTriangleMesh(const string& name, const CTriangleMeshA& mesh);
	TSmoothTriangleMesh(const string& name, const TSmoothTriangleMesh& mesh,
			    const C3DTransformation& deform);
	TSmoothTriangleMesh(const string& name, const CTriangleMeshA& mesh,
			    const C3DTransformation& deform);
	~TSmoothTriangleMesh();
	
	void deform(const C3DTransformation& shift);
	const normal_vertex_t *get_vertices()const {return vertices;}
	virtual TVertexList *get_vertex_list(int stride)const; 

	virtual void povray_output_object(FILE *f)const;

	virtual const char* get_type_name()const;
	virtual const char* get_classname()const;
	virtual void get_classname_list(list<string> *classlist)const;
	
protected:
	virtual void do_gl_draw(const TCamera& c) const; 	
	
private: 	
	virtual bool write_vertices(FILE *f)const; 
	void create_from_vertex_array(const vertex_t *v);
	void recalc_normals();
	void gl_attach();	
};


/// a mesh with normals and colors defined per vertex
class TColorSmoothMesh: public CTriangleMeshABase {
	color_normal_vertex_t *vertices;
	float *vertex_deform_scale;
	float max_shrink; 
	float max_grow;
 	float inv_max_shrink; 
	float inv_max_grow;
	float inv_max_shrink_log; 
	float inv_max_grow_log;	
	float max_grow_log;
	float max_shrink_log;
	
	float ms,ims; 
	int scale_type; 
	bool do_draw_decoration;
public:	
	enum EScaleType  {
		st_standart,
		st_divide, 
		st_log,
		st_scale_2mm,
		st_scale_4mm,
		st_scale_8mm,
		st_scale_16mm,
		st_scale_32mm,
		st_last
	};

	TColorSmoothMesh(const string& name, int _n_triangles, triangle_ui_t *t,
			 int _n_vertices, color_normal_vertex_t *v);

	TColorSmoothMesh(const string& name, int _n_triangles, triangle_ui_t *t,
			 int _n_vertices, color_normal_vertex_t *v, float *scale);

	template <class Deformation>
	TColorSmoothMesh(const string& name, const TSmoothTriangleMesh& mesh, 
			 const Deformation& deform,bool bdeform); 
	
	~TColorSmoothMesh();
	
	virtual TVertexList *get_vertex_list(int stride)const; 
	unique_ptr<TNormalVertexList> get_vertex_normal_list(int stride)const;

	virtual const char* get_type_name()const;
	virtual const char* get_classname()const;
	virtual void get_classname_list(list<string> *classlist)const;
	
	void apply_color_change();
	float get_max_shrink()const; 
	float get_max_grow()const;
	void show_decoration(bool value); 
	virtual void set_transparency(float alpha);
	void set_scale_type(EScaleType t) {
		scale_type = t;
	}
	void toggle_scale_type();
	

	virtual void draw_decoration(const C2DBounds& viewport,const TGLText& writer);
	virtual void povray_output_object(FILE *f)const;
	virtual void povray_output_description(FILE *f)const;

protected: 	
	virtual bool do_handle_key_event(int key);
	virtual void do_gl_draw(const TCamera& c) const;
private:
	virtual bool write_vertices(FILE *f)const; 
	void povray_write_color(FILE *f,float scale)const;
	void gl_attach();
};

inline void TColorSmoothMesh::show_decoration(bool value) 
{
	do_draw_decoration = value; 
}

inline float TColorSmoothMesh::get_max_shrink()const
{
	return max_shrink; 
}

inline float TColorSmoothMesh::get_max_grow()const
{
	return max_grow;
}

template <class Deformation>
TColorSmoothMesh::TColorSmoothMesh(const string& name, const TSmoothTriangleMesh& mesh, 
				   const Deformation& deform, bool bdeform):
	CTriangleMeshABase(name,mesh),
	max_shrink(0.0f),
	max_grow(0.0f),
	max_grow_log(0.0f),
	max_shrink_log(0.0f),
	scale_type(st_standart),
	do_draw_decoration(true)
{
	vertices = new color_normal_vertex_t[n_vertices];
	vertex_deform_scale = new float[n_vertices];
	color_normal_vertex_t *cnv = vertices; 
	const normal_vertex_t *nv = mesh.get_vertices();
	
	for (size_t i = 0; i < n_vertices; i++,++cnv,++nv) {
		cnv->normal = nv->normal;
		cnv->vertex = nv->vertex; 
		C3DFVector defo = deform(cnv->vertex  - (0.5f * cnv->normal)); 
		if (bdeform) 
			cnv->vertex += defo; 
		float shift =  dot(defo, cnv->normal);
		if (shift  > 0) {
			vertex_deform_scale[i] = -shift; 
			if (shift > max_shrink) max_shrink = shift; 
			shift = log(shift + 1.0);
			if (shift > max_shrink_log) max_shrink_log = shift; 
		}else{
			shift = -shift;
			vertex_deform_scale[i] = shift; 
			if (shift > max_grow) max_grow = shift; 
			shift = log(shift + 1.0);
			if (shift > max_grow_log) max_grow_log = shift; 
		}
	}
	
	if (max_shrink != 0 ) {
		inv_max_shrink = 1.0f / max_shrink;
		inv_max_shrink_log = 1.0f / max_shrink_log;		
		cerr << "max_shrink : " << max_shrink << endl; 
	}
	
	if (max_grow != 0 ) {
		inv_max_grow_log = 1.0f / max_grow_log;
		inv_max_grow = 1.0f / max_grow;
		cerr << "max_grow : " << max_grow << endl; 
	}
	
}

#endif
