//
// ======================================================
//
//       Implementation for Class
//
//          Vertex
//
// ======================================================
//

#include <math.h>
#include "vertex.h"

//#define _VERTEX_ALLOCATION_

#define VERTEX_ALLOC_CHUNK 1024

Vertex* Vertex::_free_list_head = NULL;
int Vertex::_chunks_allocated = 0;

int Vertex::_vert_allocated = 0;
int Vertex::_vert_in_memory = 0;

Vec4 Vertex::_NormalizeTemp_P;
Vec4 Vertex::_NormalizeTemp_N;

void Vertex::CopyFrom(const Vertex &v) {

	_point = v._point;
	if ((_has_color = v._has_color) || (_has_normal = v._has_normal))
		_color_or_normal = v._color_or_normal;
	_flag = v._flag;
	_tex_s = v._tex_s;
	_tex_t = v._tex_t;
	_tex_q = v._tex_q;
	_next = NULL;
}


Vertex::Vertex() {

    _has_color = _has_normal = _flag = 0;
	_next = NULL;

#ifdef _VERTEX_ALLOCATION_
    _vert_allocated++;
    _vert_in_memory++;
    cerr << "Vertex+ " << _vert_in_memory << " (" << _vert_allocated << ")"
		 << endl;
#endif
}


Vertex::Vertex(const Vertex& v) {

	CopyFrom(v);

#ifdef _VERTEX_ALLOCATION_
    _vert_allocated++;
    _vert_in_memory++;
    cerr << "Vertex+ " << _vert_in_memory << " (" << _vert_allocated << ")"
		 << endl;
#endif
}

Vertex::Vertex(real x, real y, real z, real w) {

    _point.Set(x, y, z, w);
    _has_color = _has_normal = _flag = 0;
	// _tex_s = _tex_t = 0;
	_next = NULL;

#ifdef _VERTEX_ALLOCATION_
    _vert_allocated++;
    _vert_in_memory++;
    cerr << "Vertex+ " << _vert_in_memory << " (" << _vert_allocated << ")"
		 << endl;
#endif
}

Vertex::Vertex(const Vec4 &v) : _point(v) {

	_has_color = _has_normal = _flag = 0;
	_next = NULL;

#ifdef _VERTEX_ALLOCATION_
    _vert_allocated++;
    _vert_in_memory++;
    cerr << "Vertex+ " << _vert_in_memory << " (" << _vert_allocated << ")"
		 << endl;
#endif
}

Vertex::Vertex(const Vec4 &v, const Vec4 &n) : _point(v), _color_or_normal(n) {

	_has_normal = 1; _has_color = _flag = 0;
	_next = NULL;

#ifdef _VERTEX_ALLOCATION_
    _vert_allocated++;
    _vert_in_memory++;
    cerr << "Vertex+ " << _vert_in_memory << " (" << _vert_allocated << ")"
		 << endl;
#endif
}


Vertex::~Vertex() {

	if (_next)
		cerr << "~Vertex(): Deleting a Vertex that is part of a list!" << endl;

#ifdef _VERTEX_ALLOCATION_
    _vert_in_memory--;
    cerr << "Vertex- " << _vert_in_memory << " (" << _vert_allocated << ")"
		 << endl;
#endif
}


void* Vertex::operator new(size_t size) {

	Vertex *v;

	if (size != sizeof(Vertex))
		return ::new char[size];

	if (_free_list_head) {
		v = _free_list_head;
		_free_list_head = *((Vertex**) &(_free_list_head->_flag));
		return v;
	}

	else {
		_free_list_head = (Vertex *)
			::new char[VERTEX_ALLOC_CHUNK * sizeof(Vertex)];

		if (!_free_list_head)
			return NULL;

		_chunks_allocated++;
//		cerr << "Vertex chunks: " << _chunks_allocated << endl;

		for (int i=0; i<VERTEX_ALLOC_CHUNK-1; i++)
			*((Vertex**) &(_free_list_head[i]._flag)) =
										(Vertex*) (_free_list_head + i + 1);

		*((Vertex**) (&_free_list_head[VERTEX_ALLOC_CHUNK-1]._flag)) = NULL;

		v = _free_list_head;
		_free_list_head++;
	}

	return v;
}

void Vertex::operator delete(void *deleteme, size_t size) {

	if (size != sizeof(Vertex)) {
		::delete [] ((char*) deleteme);
		return;
	}

	Vertex *v = (Vertex*) deleteme;

	*((Vertex**) &(v->_flag)) = (Vertex*) _free_list_head;
	_free_list_head = v;
}


ostream& operator<<(ostream& co, const Vertex& v) {

	co << "P: " << v._point << " CN: " << v._color_or_normal;
	if (v._next)
		co << " + " << endl;
	else
		co << " * " << endl;

	return co;
}

/*
int operator==(const Vertex &v1, const Vertex &v2) {

	int result = (v1._point == v2._point);
	if (result && v1._ValidNormal && v2._ValidNormal)
		result = (v1._normal == v2._normal);

	return result;
}
*/

Vertex& Vertex::operator=(const Vertex& v) {

	CopyFrom(v);

    return *this;
}

// ** inlined **

/*
Vertex& Vertex::operator*=(const JLmatrix& m) {

    _point.FastJLmatrixMultiply(m);
    if (_ValidNormal)
	_normal.FastJLmatrixMultiply(m);

    return *this;
}
*/

// ** inlined **

/*
Vertex& Vertex::DivideByW() {

    _point.DivideByW();
    if (_ValidNormal)
	_normal.DivideByW();

    return *this;
}
*/

real Vertex::Angle(Vec4 &Camera, Vec4 &CameraToCOA) {

    real dot = CameraToCOA.Dot3((_point - Camera).MakeAndGetUnit());

    return acosf(dot);
}

real Vertex::Distance(Vec4 &Camera, Vec4 &CameraToCOA) {

    return (_point - Camera).Dot3(CameraToCOA);
}
