#pragma once

#include "volumeshop.h"

#include "Vector.h"

class DataVoxel
{
public:

	DataVoxel()
	{
		SetValue(0.0f);
		SetGradient(Vector(0.0f,0.0f,0.0f));
	};

	DataVoxel(const DataVoxel & datOther)
	{
		m_fValue = datOther.m_fValue;
		m_vecGradient = datOther.m_vecGradient;
	};

	DataVoxel(const float fValue)
	{
		SetValue(fValue);
		SetGradient(Vector(0.0f,0.0f,0.0f));
	};

	DataVoxel(const float fValue, const Vector & vecGradient)
	{
		SetValue(fValue);
		SetGradient(vecGradient);
	};

	DataVoxel(const float fValue1nx1ny1nz, const float fValue0px1ny1nz, const float fValue1px1ny1nz, const float fValue1nx0py1nz, const float fValue0px0py1nz, const float fValue1px0py1nz, const float fValue1nx1py1nz, const float fValue0px1py1nz, const float fValue1px1py1nz, const float fValue1nx1ny0pz, const float fValue0px1ny0pz, const float fValue1px1ny0pz, const float fValue1nx0py0pz, const float fValue0px0py0pz, const float fValue1px0py0pz, const float fValue1nx1py0pz, const float fValue0px1py0pz, const float fValue1px1py0pz, const float fValue1nx1ny1pz, const float fValue0px1ny1pz, const float fValue1px1ny1pz, const float fValue1nx0py1pz, const float fValue0px0py1pz, const float fValue1px0py1pz, const float fValue1nx1py1pz, const float fValue0px1py1pz, const float fValue1px1py1pz)
	{
		static const float vfWeights[3][3][3]= {  {  {2.0f,3.0f,2.0f}, {3.0f,6.0f,3.0f}, {2.0f,3.0f,2.0f}  },  { {3.0f,6.0f,3.0f},  {6.0f,0.0f,6.0f},  {3.0f,6.0f,3.0f} },  { {2.0f,3.0f,2.0f},  {3.0f,6.0f,3.0f},  {2.0f,3.0f,2.0f} } };
		static const float fWeightValue = 1.0f / (vfWeights[0][0][0] + vfWeights[1][0][0] + vfWeights[2][0][0] + vfWeights[0][1][0] + vfWeights[1][1][0] + vfWeights[2][1][0] + vfWeights[0][2][0] + vfWeights[1][2][0] + vfWeights[2][2][0] + vfWeights[0][0][1] + vfWeights[1][0][1] + vfWeights[2][0][1] + vfWeights[0][1][1] + vfWeights[1][1][1] + vfWeights[2][1][1] + vfWeights[0][2][1] + vfWeights[1][2][1] + vfWeights[2][2][1] + vfWeights[0][0][2] + vfWeights[1][0][2] + vfWeights[2][0][2] + vfWeights[0][1][2] + vfWeights[1][1][2] + vfWeights[2][1][2] + vfWeights[0][2][2] + vfWeights[1][2][2] + vfWeights[2][2][2]);
		static const float fWeightGx = 1.0f / (vfWeights[0][0][0] + vfWeights[1][0][0] + vfWeights[2][0][0] + vfWeights[0][1][0] + vfWeights[1][1][0] + vfWeights[2][1][0] + vfWeights[0][2][0] + vfWeights[1][2][0] + vfWeights[2][2][0] + vfWeights[0][0][2] + vfWeights[1][0][2] + vfWeights[2][0][2] + vfWeights[0][1][2] + vfWeights[1][1][2] + vfWeights[2][1][2] + vfWeights[0][2][2] + vfWeights[1][2][2] + vfWeights[2][2][2]); 
		static const float fWeightGy = 1.0f / (vfWeights[0][0][0] + vfWeights[1][0][0] + vfWeights[2][0][0] + vfWeights[0][2][0] + vfWeights[1][2][0] + vfWeights[2][2][0] + vfWeights[0][0][1] + vfWeights[1][0][1] + vfWeights[2][0][1] + vfWeights[0][2][1] + vfWeights[1][2][1] + vfWeights[2][2][1] + vfWeights[0][0][2] + vfWeights[1][0][2] + vfWeights[2][0][2] + vfWeights[0][2][2] + vfWeights[1][2][2] + vfWeights[2][2][2]);
		static const float fWeightGz = 1.0f / (vfWeights[0][0][0] + vfWeights[2][0][0] + vfWeights[0][1][0] + vfWeights[2][1][0] + vfWeights[0][2][0] + vfWeights[2][2][0] + vfWeights[0][0][1] + vfWeights[2][0][1] + vfWeights[0][1][1] + vfWeights[2][1][1] + vfWeights[0][2][1] + vfWeights[2][2][1] + vfWeights[0][0][2] + vfWeights[2][0][2] + vfWeights[0][1][2] + vfWeights[2][1][2] + vfWeights[0][2][2] + vfWeights[2][2][2]);

		const float fValue(fWeightValue * (+ vfWeights[0][0][0] * fValue1nx1ny1nz + vfWeights[1][0][0] * fValue1nx1ny0pz + vfWeights[2][0][0] * fValue1nx1ny1pz + vfWeights[0][1][0] * fValue1nx0py1nz + vfWeights[1][1][0] * fValue1nx0py0pz + vfWeights[2][1][0] * fValue1nx0py1pz + vfWeights[0][2][0] * fValue1nx1py1nz + vfWeights[1][2][0] * fValue1nx1py0pz + vfWeights[2][2][0] * fValue1nx1py1pz + vfWeights[0][0][1] * fValue0px1ny1nz + vfWeights[1][0][1] * fValue0px1ny0pz + vfWeights[2][0][1] * fValue0px1ny1pz + vfWeights[0][1][1] * fValue0px0py1nz + vfWeights[2][1][1] * fValue0px0py1pz + vfWeights[0][2][1] * fValue0px1py1nz + vfWeights[1][2][1] * fValue0px1py0pz + vfWeights[2][2][1] * fValue0px1py1pz + vfWeights[0][0][2] * fValue1px1ny1nz + vfWeights[1][0][2] * fValue1px1ny0pz + vfWeights[2][0][2] * fValue1px1ny1pz + vfWeights[0][1][2] * fValue1px0py1nz + vfWeights[1][1][2] * fValue1px0py0pz + vfWeights[2][1][2] * fValue1px0py1pz + vfWeights[0][2][2] * fValue1px1py1nz + vfWeights[1][2][2] * fValue1px1py0pz + vfWeights[2][2][2] * fValue1px1py1pz));
		const float fGx(fWeightGx * (- vfWeights[0][0][0] * fValue1nx1ny1nz - vfWeights[1][0][0] * fValue1nx1ny0pz - vfWeights[2][0][0] * fValue1nx1ny1pz - vfWeights[0][1][0] * fValue1nx0py1nz - vfWeights[1][1][0] * fValue1nx0py0pz - vfWeights[2][1][0] * fValue1nx0py1pz - vfWeights[0][2][0] * fValue1nx1py1nz - vfWeights[1][2][0] * fValue1nx1py0pz - vfWeights[2][2][0] * fValue1nx1py1pz + vfWeights[0][0][2] * fValue1px1ny1nz + vfWeights[1][0][2] * fValue1px1ny0pz + vfWeights[2][0][2] * fValue1px1ny1pz + vfWeights[0][1][2] * fValue1px0py1nz + vfWeights[1][1][2] * fValue1px0py0pz + vfWeights[2][1][2] * fValue1px0py1pz + vfWeights[0][2][2] * fValue1px1py1nz + vfWeights[1][2][2] * fValue1px1py0pz + vfWeights[2][2][2] * fValue1px1py1pz));
		const float fGy(fWeightGy * (- vfWeights[0][0][0] * fValue1nx1ny1nz - vfWeights[1][0][0] * fValue1nx1ny0pz - vfWeights[2][0][0] * fValue1nx1ny1pz + vfWeights[0][2][0] * fValue1nx1py1nz + vfWeights[1][2][0] * fValue1nx1py0pz + vfWeights[2][2][0] * fValue1nx1py1pz - vfWeights[0][0][1] * fValue0px1ny1nz - vfWeights[1][0][1] * fValue0px1ny0pz - vfWeights[2][0][1] * fValue0px1ny1pz + vfWeights[0][2][1] * fValue0px1py1nz + vfWeights[1][2][1] * fValue0px1py0pz + vfWeights[2][2][1] * fValue0px1py1pz - vfWeights[0][0][2] * fValue1px1ny1nz - vfWeights[1][0][2] * fValue1px1ny0pz - vfWeights[2][0][2] * fValue1px1ny1pz + vfWeights[0][2][2] * fValue1px1py1nz + vfWeights[1][2][2] * fValue1px1py0pz + vfWeights[2][2][2] * fValue1px1py1pz));
		const float fGz(fWeightGz * (- vfWeights[0][0][0] * fValue1nx1ny1nz + vfWeights[2][0][0] * fValue1nx1ny1pz - vfWeights[0][1][0] * fValue1nx0py1nz + vfWeights[2][1][0] * fValue1nx0py1pz - vfWeights[0][2][0] * fValue1nx1py1nz + vfWeights[2][2][0] * fValue1nx1py1pz - vfWeights[0][0][1] * fValue0px1ny1nz + vfWeights[2][0][1] * fValue0px1ny1pz - vfWeights[0][1][1] * fValue0px0py1nz + vfWeights[2][1][1] * fValue0px0py1pz - vfWeights[0][2][1] * fValue0px1py1nz + vfWeights[2][2][1] * fValue0px1py1pz - vfWeights[0][0][2] * fValue1px1ny1nz + vfWeights[2][0][2] * fValue1px1ny1pz - vfWeights[0][1][2] * fValue1px0py1nz + vfWeights[2][1][2] * fValue1px0py1pz - vfWeights[0][2][2] * fValue1px1py1nz + vfWeights[2][2][2] * fValue1px1py1pz));
		const Vector vecGradient = Vector(fGx,fGy,fGz);//.GetNormalized();

		SetValue(fValue);
		SetGradient(vecGradient);
	};

	~DataVoxel()
	{
	};

	void SetValue(const float fValue)
	{
		m_fValue = fValue;
	};

	const float GetValue() const
	{
		return m_fValue;
	};

	void SetGradient(const Vector &vecGradient)
	{
		m_vecGradient = vecGradient;
	};

	const Vector & GetGradient() const
	{
		return m_vecGradient;
	};

	const bool operator==(const DataVoxel &datOther) const
	{
		return (GetValue() == datOther.GetValue()) && (GetGradient() == datOther.GetGradient());
	};

	const bool operator!=(const DataVoxel &datOther) const
	{
		return !(*this == datOther);
	};

	const bool operator>(const DataVoxel &datOther) const
	{
		return GetValue() > datOther.GetValue();
	};

	const bool operator>=(const DataVoxel &datOther) const
	{
		return GetValue() >= datOther.GetValue();
	};

	const bool operator<(const DataVoxel &datOther) const
	{
		return GetValue() < datOther.GetValue();
	};

	const bool operator<=(const DataVoxel &datOther) const
	{
		return GetValue() <= datOther.GetValue();
	};

	const DataVoxel & operator+=(const DataVoxel & datOther)
	{
		m_fValue += datOther.m_fValue;
		return *this;
	};

	const DataVoxel & operator-=(const DataVoxel & datOther)
	{
		m_fValue -= datOther.m_fValue;
		return *this;
	};

	const DataVoxel & operator*=(const float & fOther)
	{
		m_fValue *= fOther;
		return *this;
	};

	const DataVoxel & operator/=(const float & fOther)
	{
		m_fValue /= fOther;
		return *this;
	};

	const DataVoxel operator+(const DataVoxel & datOther) const
	{
		DataVoxel voxNew = *this;
		voxNew += datOther;
		return voxNew;
	};

	const DataVoxel operator-(const DataVoxel & datOther) const
	{
		DataVoxel voxNew = *this;
		voxNew -= datOther;
		return voxNew;
	};

	const DataVoxel operator*(const float & fOther) const
	{
		DataVoxel voxNew = *this;
		voxNew *= fOther;
		return voxNew;
	};

	const DataVoxel operator/(const float & fOther) const
	{
		DataVoxel voxNew = *this;
		voxNew /= fOther;
		return voxNew;
	};

private:

	Vector m_vecGradient;
	float m_fValue;
};

template <>
inline GLenum glInternalFormat<DataVoxel>()
{
	/*
	if (GLEW_ARB_texture_float)
		return GL_RGBA16F_ARB;
	else
	*/
		return GL_SIGNED_RGB_UNSIGNED_ALPHA_NV;
};

template <>
inline GLenum glFormat<DataVoxel>()
{
	return GL_RGBA;
};

template <>
inline GLenum glType<DataVoxel>()
{
	return GL_FLOAT;
};