//
// ======================================================
//
//       Interface for Class
//
//          Vertex
//
// ======================================================
//

#ifndef JL_VERTEX_H
#define JL_VERTEX_H

#include <iostream.h>
#include "vec4.h"
#include "matrix.h"

class Vertex {

	friend class PGonGeneral;
	friend class SubdObj;
	friend class WingedEdgeMesh;
	friend class IndexedTriangle;

	private:
		Vec4	_point, _color_or_normal;
		short	_has_color, _has_normal;
		int		_flag;
		real	_tex_s, _tex_t, _tex_q;
		Vertex	*_next;

		static Vertex *_free_list_head;
		static int _chunks_allocated;

		static int _vert_allocated, _vert_in_memory;
		static Vec4 _NormalizeTemp_P, _NormalizeTemp_N;

		void CopyFrom(const Vertex&);

	public:
//		Vertex() { _has_color = _has_normal = _flag = 0; _next = NULL; }
//		Vertex(const Vertex &v) { CopyFrom(v); }
//		Vertex(const Vec4 &v) : _point(v)
//			{ _has_color = _has_normal = _flag = 0; _next = NULL; }
//		Vertex(const Vec4 &v, const Vec4 &n) : _point(v), _color_or_normal(n)
//			{ _has_normal = 1; _has_color = _flag = 0; _next = NULL; }

		Vertex();
		Vertex(const Vertex &v);
		Vertex(real, real, real, real w=1);
		Vertex(const Vec4 &v);
		Vertex(const Vec4 &v, const Vec4 &n);

		~Vertex();

		void* operator new(size_t size);
		void operator delete(void *deleteme, size_t size);

		friend ostream& operator<<(ostream&, const Vertex&);
		friend ostream& operator<<(ostream&, const PGonGeneral&);
		friend istream& operator>>(istream&, SubdObj&);

/*
		friend int operator==(const Vertex&, const Vertex&);
*/

		Vertex& operator=(const Vertex&);

		inline Vertex& operator*=(const JLmatrix &m) {
				_point.FastJLmatrixMultiply(m);
				if (_has_normal)
					_color_or_normal.FastJLmatrixMultiply(m);
				return *this;
			}

		inline friend void
			VertexFastAverage(Vertex &v1, const Vertex &v2, const Vertex &v3) {
				Vec4FastAverage(v1._point, v2._point, v3._point);
				if (v2._has_color && v3._has_color) {
					Vec4FastAverage(v1._color_or_normal,
									v2._color_or_normal,
									v3._color_or_normal);
					v1._has_color = 1;
					v1._has_normal = 0;
				}
				else if (v2._has_normal && v3._has_normal) {
					Vec4FastAverage(v1._color_or_normal,
									v2._color_or_normal,
									v3._color_or_normal);
					v1._has_color = 0;
					v1._has_normal = 1;
				}
				else
					v1._has_color = v1._has_normal = 0;
				v1._tex_s = (v2._tex_s + v3._tex_s) * 0.5;
				v1._tex_t = (v2._tex_t + v3._tex_t) * 0.5;
				v1._tex_q = (v2._tex_q + v3._tex_q) * 0.5;
			}

		inline Vertex& DivideByW() {
				_point.DivideByW();
				if (_has_normal)
					_color_or_normal.DivideByW();
				return *this;
			}

		inline void NormalizeNormal(JLmatrix *VT, JLmatrix *IVT) {
			_point.FastJLmatrixMultiplyAndDivideByW(_NormalizeTemp_P, *IVT);
			_color_or_normal.
					FastJLmatrixMultiplyAndDivideByW(_NormalizeTemp_N, *IVT);
			Vec4FastSub(_NormalizeTemp_N, _NormalizeTemp_N, _NormalizeTemp_P);
			_NormalizeTemp_N.MakeUnit();
			Vec4FastAdd(_NormalizeTemp_N, _NormalizeTemp_N, _NormalizeTemp_P);
			_NormalizeTemp_N.FastJLmatrixMultiplyAndDivideByW(
					_color_or_normal, *VT);
		}

		inline void SetFlag(int f) { _flag = f; }

		inline Vertex& SetPoint(real x, real y, real z, real w) {
			_point.Set(x, y, z, w); return *this; }
		inline Vertex& SetPoint(const Vec4 &v) { _point = v; return *this; }
		inline Vec4 GetPoint() const { return _point; }
		inline Vec4& Point() { return _point; }
		inline const Vec4& Point() const { return _point; }

		inline real TexS() { return _tex_s; }
		inline real TexT() { return _tex_t; }
		inline real TexQ() { return _tex_q; }
		Vertex& SetTex(real s, real t, real q=1) {
				_tex_s=s; _tex_t=t; _tex_q=q; return *this;
			}

		inline void SetNormal(const Vec4 &v)
				{ _color_or_normal = v; _has_color = 0; _has_normal = 1; }
		inline Vertex& SetNormal(real x, real y, real z, real w) {
				_color_or_normal.Set(x, y, z, w);
				_has_color = 0; _has_normal = 1;
				return *this;
			}
		inline Vec4& GetNormal() { return _color_or_normal; }
		inline Vertex& RemoveNormal() { _has_normal = 0; return *this; };

		inline void SetColor(const Vec4 &c)
				{ _color_or_normal = c; _has_color = 1; _has_normal = 0; }
		inline Vertex& SetColor(real r, real g, real b) {
				_color_or_normal.Set(r, g, b);
				_has_color = 1; _has_normal = 0;
				return *this;
			}
		inline Vec4& GetColor() { return _color_or_normal; }
		inline Vertex& RemoveColor() { _has_color = 0; return *this; };

		real Angle(Vec4&, Vec4&);
		real Distance(Vec4&, Vec4&);
};

typedef Vertex* VertexPtr;

#endif
