/* -*- 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 __MONA_4DVECTOR_HH
#define __MONA_4DVECTOR_HH 1

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

#include <typeinfo>
#include <assert.h>
#include <stdexcept>
#include <math.h>
#include <iostream>
#include <cstring>
#include <cstdio>

namespace mia {
#define g_vector_start '<'
#define g_vector_end '>'

	template < class T > class T4DVector {
	public:
		T x,y,z,a;
		
		T4DVector():x(0),y(0),z(0),a(0){};
		T4DVector(int dim):x(0),y(0),z(0),a(0){
			assert(dim == 4);
		}
		
		T4DVector(const T& _x,const T& _y,const T& _z, const T& _a):
			x(_x),y(_y),z(_z),a(_a){
			}
		template <class in> T4DVector(const T4DVector<in>& org):
			x(T(org.x)),y(T(org.y)),z(T(org.z)),a(T(org.a)){
			}
		T operator[](int i)const {
			// Cruel hack, i have to think of something better
			return (&x)[i];
		}
		T& operator[](int i) {
			// dito
			return (&x)[i];
		}
		template <class in> T4DVector<T>& operator =(const T4DVector<in>& org){
			if (this != &org) {
				x=org.x; y=org.y; z=org.z, a=org.a;
			}
			return *this;
		}
		
		double norm()const{
			return sqrt( x*x+ y*y+ z*z+ a*a);
		}
		double norm2()const{
			return x*x + y*y + z*z + a*a;
		}
		
		double norm_inf() const{
			const char *help= typeid(T).name();
			double result; 
			if (strchr("csldfirx",help[0])!=0) {
				result = fabs(x);
				double b = fabs(y);
				if (b > result) result = b;
				b = fabs(z);
				if (b > result) result = b;
				b = fabs(a);
				if (b > result) result = b;
			}else if (help[0] == 'U') { // unsigned type
				result = ( x > y ?  ( x > z ? x : z ) : ( y > z ? y : z ));
				result = a > result ? a : result;
			}else{
				fprintf(stderr,"error type infnorm\n");
			}
			// if elemt type is pointer or class , instanciation will fail anyway
			// since no fabs is defined
			return result;
		}
		int get_dim() const {
			return 4;
		}
		
		T4DVector<T>& operator +=(const T4DVector<T>& u){
			x+=u.x; y+=u.y; z+=u.z; a += u.a;
			return *this;
		}
		T4DVector<T>& operator -=(const T4DVector<T>& u){
			x-=u.x; y-=u.y; z-=u.z; a-=u.a;
			return *this;
		}
		T4DVector<T>& operator *=(const T& u){
			x *= u; y *= u; z *= u; a *= u;
			return *this;
		}
		T4DVector<T>& operator /=(const T& u){
			assert(u != T());
			x /= u; y /= u; z /= u; a /= u;
			return *this;
		}
		
		T4DVector<T> operator +(const T4DVector<T>& u)const{
			return T4DVector<T>(x+u.x, y+u.y, z+u.z,a+u.a);
		}
		
		T4DVector<T> operator -(const T4DVector<T>& u)const{
			return T4DVector<T>(x-u.x, y-u.y, z-u.z,a-u.a);
		}
		T            operator *(const T4DVector<T>& u)const{
			return x*u.x+y*u.y+z*u.z+a*u.a;
		}
		
		T4DVector<T> operator /(const T& u)const{
			assert(u != T());
			return T4DVector<T>(x/u,y/u,z/u,a/u);
		}
		T4DVector<T> operator *(const T& u)const{
			return T4DVector<T>(x*u,y*u,z*u,a*u);
		}
		
		bool operator == (const T4DVector<T>& u)const{
			return (x == u.x && y == u.y && z == u.z && a == u.a);
		}
		bool operator != (const T4DVector<T>& u)const{
			return (x != u.x && y != u.y && z != u.z && a != u.a);
		}
		bool operator <  (const T4DVector<T>& u)const{
			return (x < u.x && y < u.y && z < u.z && a < u.a);
		}
		
		bool operator >  (const T4DVector<T>& u)const{
			return (x > u.x && y > u.y && z > u.z && a > u.a);
		}
		bool operator <= (const T4DVector<T>& u)const{
			return (x <= u.x && y <= u.y && z <= u.z && a <= u.a);
		}
		bool operator >= (const T4DVector<T>& u)const{
			return (x >= u.x && y >= u.y && z >= u.z && a >= u.a);
		}
		
		
		friend T4DVector<T> operator *(const T& a,const T4DVector<T>& b){
			return b * a;
		}
		
		/// print out the vector to the stream \a *os
		void write(std::ostream& os)const {
			os << "(" << x << ", " << y << ", " << z << "," << a << ")"; 
		}
		
		void read(std::istream& is) {
			char c; 
			
			T r,s,t,u; 
			is >> c;
			if (c == g_vector_start) {
				is >> r;
				is >> c; 
				if (c != ',') {
					is.clear(std::ios::badbit);
					return; 
				}
				is >> s; 
				is >> c; 
				if (c != ',') {
					is.clear(std::ios::badbit);
					return; 
				}
				is >> t; 
				is >> c; 
				if (c != ',') {
					is.clear(std::ios::badbit);
					return; 
				}
				is >> u; 
				is >> c; 
				if (c != g_vector_end) {
					is.clear(std::ios::badbit);
					return; 
				}
				
				x = r; 
				y = s; 
				z = t;
				a = u; 
			}else
				is.putback(c);
			
		}
	};
	
	template <class T> double fabs(const T4DVector<T>& t)
	{
		return t.norm();
	}
	
	typedef T4DVector<float> C4DFVector;
	typedef T4DVector<size_t > C4DBounds;
	
	
	/// stream output operator for 4DVector
	template <class T> 
	std::ostream& operator << (std::ostream& os, const T4DVector<T>& v)
	{
		v.write(os);
		return os; 
	}
	
	/// stream input operator for 4DVector
	template <class T> 
	std::istream& operator >> (std::istream& is, T4DVector<T>& v)
	{
		v.read(is);
		return is; 
	}

} // namespace mona
	
#endif // __MONA_4DVECTOR_HH
	

