#ifndef CVD_RGB_TRAITS_H
#define CVD_RGB_TRAITS_H

#include <cvd/argb.h>
#include <cvd/bgrx.h>
#include <cvd/internal/builtin_components.h>
#include <cvd/internal/pixel_traits.h>
#include <cvd/la.h>
#include <cvd/rgb.h>
#include <cvd/rgb8.h>
#include <cvd/rgba.h>

namespace CVD
{
namespace Pixel
{

	template <class P>
	struct Component<Rgb<P>>
	{
		typedef P type;
		static const size_t count = 3;

		//This version is much faster, with -funroll-loops
		static const P& get(const Rgb<P>& pixel, size_t i)
		{
			return *(reinterpret_cast<const P*>(&pixel) + i);
			//return i == 0 ? pixel.red : (i==1 ? pixel.green : pixel.blue);
		}

		static P& get(Rgb<P>& pixel, size_t i)
		{
			return *(reinterpret_cast<P*>(&pixel) + i);
			// return i == 0 ? pixel.red : (i==1 ? pixel.green : pixel.blue);
		}
	};

	template <class P>
	struct Component<Bgrx<P>>
	{
		typedef P type;
		static const size_t count = 3;

		//This version is much faster, with -funroll-loops
		static const P& get(const Bgrx<P>& pixel, size_t i)
		{
			return *(reinterpret_cast<const P*>(&pixel) + i);
			//return i == 0 ? pixel.blue : (i==1 ? pixel.green : pixel.red);
		}

		static P& get(Bgrx<P>& pixel, size_t i)
		{
			return *(reinterpret_cast<P*>(&pixel) + i);
			// return i == 0 ? pixel.blue : (i==1 ? pixel.green : pixel.red);
		}
	};

	template <>
	struct Component<Rgb8>
	{
		typedef unsigned char type;
		static const size_t count = 3;

		static const type& get(const Rgb8& pixel, size_t i)
		{
			return *(reinterpret_cast<const unsigned char*>(&pixel) + i);
			//return i == 0 ? pixel.red : (i==1 ? pixel.green : pixel.blue);
		}

		static type& get(Rgb8& pixel, size_t i)
		{
			return *(reinterpret_cast<unsigned char*>(&pixel) + i);
			//return i == 0 ? pixel.red : (i==1 ? pixel.green : pixel.blue);
		}
	};

	template <class P>
	struct Component<Rgba<P>>
	{
		typedef P type;
		static const size_t count = 4;

		static const P& get(const Rgba<P>& pixel, size_t i)
		{
			return *(reinterpret_cast<const P*>(&pixel) + i);
			//return i == 0 ? pixel.red : (i==1 ? pixel.green : (i==2 ?pixel.blue: pixel.alpha));
		}

		static P& get(Rgba<P>& pixel, size_t i)
		{
			return *(reinterpret_cast<P*>(&pixel) + i);
			//return i == 0 ? pixel.red : (i==1 ? pixel.green : (i==2 ?pixel.blue: pixel.alpha));
		}
	};

	template <class P>
	struct Component<La<P>>
	{
		typedef P type;
		static const size_t count = 2;

		static const P& get(const La<P>& pixel, size_t i)
		{
			return *(reinterpret_cast<const P*>(&pixel) + i);
		}

		static P& get(La<P>& pixel, size_t i)
		{
			return *(reinterpret_cast<P*>(&pixel) + i);
		}
	};

	template <class P>
	struct Component<Argb<P>>
	{
		typedef P type;
		static const size_t count = 4;

		static const P& get(const Argb<P>& pixel, size_t i)
		{
			//return *(reinterpret_cast<const P*>(&pixel)+i);
			return i == 0 ? pixel.red : (i == 1 ? pixel.green : (i == 2 ? pixel.blue : pixel.alpha));
		}

		static P& get(Argb<P>& pixel, size_t i)
		{
			//return *(reinterpret_cast<P*>(&pixel)+i);
			return i == 0 ? pixel.red : (i == 1 ? pixel.green : (i == 2 ? pixel.blue : pixel.alpha));
		}
	};

	template <class T>
	struct is_Rgb
	{
		enum
		{
			value = 0
		};
	};
	template <class T>
	struct is_Rgb<Rgb<T>>
	{
		enum
		{
			value = 1
		};
	};
	template <>
	struct is_Rgb<Rgb8>
	{
		enum
		{
			value = 1
		};
	};
	template <class T>
	struct is_Rgb<Rgba<T>>
	{
		enum
		{
			value = 1
		};
	};
	template <class T>
	struct is_Rgb<Argb<T>>
	{
		enum
		{
			value = 1
		};
	};

	template <class T, int LIFT>
	struct traits<Rgb<T>, LIFT>
	{
		typedef Rgb<typename Pixel::traits<T>::wider_type> wider_type;
		typedef Rgb<typename Pixel::traits<T>::float_type> float_type;
	};

	template <class T, int LIFT>
	struct traits<Rgba<T>, LIFT>
	{
		typedef Rgba<typename Pixel::traits<T>::wider_type> wider_type;
		typedef Rgba<typename Pixel::traits<T>::float_type> float_type;
	};

	template <class T, int LIFT>
	struct traits<La<T>, LIFT>
	{
		typedef La<typename Pixel::traits<T>::wider_type> wider_type;
		typedef La<typename Pixel::traits<T>::float_type> float_type;
	};

	template <int LIFT>
	struct traits<Rgb8, LIFT>
	{
		typedef Rgb<int> wider_type;
		typedef Rgb<float> float_type;
	};

}
template <class T>
struct Rgb_ops
{
	template <class S>
	static inline T sum(const T& a, const S& b) { return T(a.red + b.red, a.green + b.green, a.blue + b.blue); }
	template <class S>
	static inline void add(T& a, const S& b)
	{
		a.red += b.red;
		a.green += b.green;
		a.blue += b.blue;
	}
	template <class S>
	static inline T diff(const T& a, const S& b) { return T(a.red - b.red, a.green - b.green, a.blue - b.blue); }
	template <class S>
	static inline void sub(T& a, const S& b)
	{
		a.red -= b.red;
		a.green -= b.green;
		a.blue -= b.blue;
	}
	template <class S>
	static inline T prod(const T& a, const S& b) { return T(a.red * b, a.green * b, a.blue * b); }
	template <class S>
	static inline void mul(T& a, const S& b)
	{
		a.red *= b;
		a.green *= b;
		a.blue *= b;
	}
	template <class S>
	static inline T quot(const T& a, const S& b) { return T(a.red / b, a.green / b, a.blue / b); }
	template <class S>
	static inline void div(T& a, const S& b)
	{
		a.red /= b;
		a.green /= b;
		a.blue /= b;
	}
	template <class S>
	static inline void assign(T& a, const S& b)
	{
		a.red = b.red;
		a.green = b.green;
		a.blue = b.blue;
	}
};

template <class T, class S>
inline Rgb<T> operator+(const Rgb<T>& a, const Rgb<S>& b) { return Rgb_ops<Rgb<T>>::sum(a, b); }
template <class T, class S>
inline Rgb<T>& operator+=(Rgb<T>& a, const Rgb<S>& b)
{
	Rgb_ops<Rgb<T>>::add(a, b);
	return a;
}
template <class T, class S>
inline Rgb<T> operator-(const Rgb<T>& a, const Rgb<S>& b) { return Rgb_ops<Rgb<T>>::diff(a, b); }
template <class T, class S>
inline Rgb<T>& operator-=(Rgb<T>& a, const Rgb<S>& b)
{
	Rgb_ops<Rgb<T>>::sub(a, b);
	return a;
}
template <class T, class S>
inline Rgb<T> operator*(const Rgb<T>& a, const S& b) { return Rgb_ops<Rgb<T>>::prod(a, b); }
template <class T, class S>
inline Rgb<T> operator*(const S& b, const Rgb<T>& a) { return Rgb_ops<Rgb<T>>::prod(a, b); }
template <class T, class S>
inline Rgb<T>& operator*=(Rgb<T>& a, const S& b)
{
	Rgb_ops<Rgb<T>>::mul(a, b);
	return a;
}
template <class T, class S>
inline Rgb<T> operator/(const Rgb<T>& a, const S& b) { return Rgb_ops<Rgb<T>>::quot(a, b); }
template <class T, class S>
inline Rgb<T> operator/(const S& b, const Rgb<T>& a) { return Rgb_ops<Rgb<T>>::quot(a, b); }
template <class T, class S>
inline Rgb<T>& operator/=(Rgb<T>& a, const S& b)
{
	Rgb_ops<Rgb<T>>::div(a, b);
	return a;
}

template <class T>
struct Rgba_ops
{
	template <class S>
	static inline T sum(const T& a, const S& b) { return T(a.red + b.red, a.green + b.green, a.blue + b.blue, a.alpha + b.alpha); }
	template <class S>
	static inline void add(T& a, const S& b)
	{
		a.red += b.red;
		a.green += b.green;
		a.blue += b.blue;
		a.alpha += b.alpha;
	}
	template <class S>
	static inline T diff(const T& a, const S& b) { return T(a.red - b.red, a.green - b.green, a.blue - b.blue, a.alpha - b.alpha); }
	template <class S>
	static inline void sub(T& a, const S& b)
	{
		a.red -= b.red;
		a.green -= b.green;
		a.blue -= b.blue;
		a.alpha -= b.alpha;
	}
	template <class S>
	static inline T prod(const T& a, const S& b) { return T(a.red * b, a.green * b, a.blue * b, a.alpha * b); }
	template <class S>
	static inline void mul(T& a, const S& b)
	{
		a.red *= b;
		a.green *= b;
		a.blue *= b;
		a.alpha *= b;
	}
	template <class S>
	static inline T quot(const T& a, const S& b) { return T(a.red / b, a.green / b, a.blue / b, a.alpha / b); }
	template <class S>
	static inline void div(T& a, const S& b)
	{
		a.red /= b;
		a.green /= b;
		a.blue /= b;
		a.alpha /= b;
	}
	template <class S>
	static inline void assign(T& a, const S& b)
	{
		a.red = b.red;
		a.green = b.green;
		a.blue = b.blue;
		a.alpha = b.alpha;
	}
};

template <class T, class S>
inline Rgba<T> operator+(const Rgba<T>& a, const Rgba<S>& b) { return Rgba_ops<Rgba<T>>::sum(a, b); }
template <class T, class S>
inline Rgba<T>& operator+=(Rgba<T>& a, const Rgba<S>& b)
{
	Rgba_ops<Rgba<T>>::add(a, b);
	return a;
}
template <class T, class S>
inline Rgba<T> operator-(const Rgba<T>& a, const Rgba<S>& b) { return Rgba_ops<Rgba<T>>::diff(a, b); }
template <class T, class S>
inline Rgba<T>& operator-=(Rgba<T>& a, const Rgba<S>& b)
{
	Rgba_ops<Rgba<T>>::sub(a, b);
	return a;
}
template <class T, class S>
inline Rgba<T> operator*(const Rgba<T>& a, const S& b) { return Rgba_ops<Rgba<T>>::prod(a, b); }
template <class T, class S>
inline Rgba<T> operator*(const S& b, const Rgba<T>& a) { return Rgba_ops<Rgba<T>>::prod(a, b); }
template <class T, class S>
inline Rgba<T>& operator*=(Rgba<T>& a, const S& b)
{
	Rgba_ops<Rgba<T>>::mul(a, b);
	return a;
}
template <class T, class S>
inline Rgba<T> operator/(const Rgba<T>& a, const S& b) { return Rgba_ops<Rgba<T>>::quot(a, b); }
template <class T, class S>
inline Rgba<T> operator/(const S& b, const Rgba<T>& a) { return Rgba_ops<Rgba<T>>::quot(a, b); }
template <class T, class S>
inline Rgba<T>& operator/=(Rgba<T>& a, const S& b)
{
	Rgba_ops<Rgba<T>>::div(a, b);
	return a;
}

template <class T>
struct La_ops
{
	template <class S>
	static inline T sum(const T& a, const S& b) { return T(a.luminance + b.luminance, a.alpha + b.alpha); }
	template <class S>
	static inline void add(T& a, const S& b)
	{
		a.luminance += b.luminance;
		a.alpha += b.alpha;
	}
	template <class S>
	static inline T diff(const T& a, const S& b) { return T(a.luminance - b.luminance, a.alpha - b.alpha); }
	template <class S>
	static inline void sub(T& a, const S& b)
	{
		a.luminance -= b.luminance;
		a.alpha -= b.alpha;
	}
	template <class S>
	static inline T prod(const T& a, const S& b) { return T(a.luminance * b, a.alpha * b); }
	template <class S>
	static inline void mul(T& a, const S& b)
	{
		a.luminance *= b;
		a.alpha *= b;
	}
	template <class S>
	static inline T quot(const T& a, const S& b) { return T(a.luminance / b, a.alpha / b); }
	template <class S>
	static inline void div(T& a, const S& b)
	{
		a.luminance /= b;
		a.alpha /= b;
	}
	template <class S>
	static inline void assign(T& a, const S& b)
	{
		a.luminance = b.luminance;
		a.alpha = b.alpha;
	}
};

template <class T, class S>
inline La<T> operator+(const La<T>& a, const La<S>& b) { return La_ops<La<T>>::sum(a, b); }
template <class T, class S>
inline La<T>& operator+=(La<T>& a, const La<S>& b)
{
	La_ops<La<T>>::add(a, b);
	return a;
}
template <class T, class S>
inline La<T> operator-(const La<T>& a, const La<S>& b) { return La_ops<La<T>>::diff(a, b); }
template <class T, class S>
inline La<T>& operator-=(La<T>& a, const La<S>& b)
{
	La_ops<La<T>>::sub(a, b);
	return a;
}
template <class T, class S>
inline La<T> operator*(const La<T>& a, const S& b) { return La_ops<La<T>>::prod(a, b); }
template <class T, class S>
inline La<T> operator*(const S& b, const La<T>& a) { return La_ops<La<T>>::prod(a, b); }
template <class T, class S>
inline La<T>& operator*=(La<T>& a, const S& b)
{
	La_ops<La<T>>::mul(a, b);
	return a;
}
template <class T, class S>
inline La<T> operator/(const La<T>& a, const S& b) { return La_ops<La<T>>::quot(a, b); }
template <class T, class S>
inline La<T> operator/(const S& b, const La<T>& a) { return La_ops<La<T>>::quot(a, b); }
template <class T, class S>
inline La<T>& operator/=(La<T>& a, const S& b)
{
	La_ops<La<T>>::div(a, b);
	return a;
}

}
#endif
