#include "common/color.hpp"

namespace xna {
	Color::Color(float r, float g, float b, float a) :
		_packedValue(PackHelper(r, g, b, a)) {
	}

	Color::Color(Vector3 const& vector) :
		_packedValue(PackHelper(vector.X, vector.Y, vector.Z, 1.0F)) {
	}
	
	Color::Color(Vector4 const& vector) :
		_packedValue(PackHelper(vector.X, vector.Y, vector.Z, vector.W)) {
	}

	void Color::PackFromVector4(Vector4 const& vector) {
		_packedValue = PackHelper(vector.X, vector.Y, vector.Z, vector.W);
	}	

	Color Color::FromNonPremultiplied(Vector4 const& vector) {
		Color color;
		color._packedValue = PackHelper(vector.X * vector.W, vector.Y * vector.W, vector.Z * vector.W, vector.W);
		return color;
	}

	Color Color::FromNonPremultiplied(Int r, Int g, Int b, Int a) {
		r = ClampToByte32(r * a / ByteMaxValue);
		g = ClampToByte32(g * a / ByteMaxValue);
		b = ClampToByte32(b * a / ByteMaxValue);
		a = ClampToByte32(a);
		g <<= 8;
		b <<= 16;
		a <<= 24;

		Color color;
		color._packedValue = static_cast<Uint>(r | g | b | a);
		return color;
	}

	Color Color::Lerp(Color const& value1, Color const& value2, float amount) {
		const Int r1 = value1.R();
		const Int g1 = value1.G();
		const Int b1 = value1.B();
		const Int a1 = value1.A();
		const Int r2 = value2.R();
		const Int g2 = value2.G();
		const Int b2 = value2.B();
		const Int a2 = value2.A();

		const auto bitmask = static_cast<Int>(PackUtils::PackUNorm(65536.0f, amount));

		const Int r = r1 + ((r2 - r1) * bitmask >> 16);
		const Int g = g1 + ((g2 - g1) * bitmask >> 16);
		const Int b = b1 + ((b2 - b1) * bitmask >> 16);
		const Int a = a1 + ((a2 - a1) * bitmask >> 16);

		Color color;
		color._packedValue = static_cast<Uint>(r | g << 8 | b << 16 | a << 24);
		return color;
	}

	Uint Color::PackHelper(float vectorX, float vectorY, float vectorZ, float vectorW) {
		const auto byteMax = static_cast<float>(ByteMaxValue);
		const auto x = PackUtils::PackUNorm(byteMax, vectorX);
		const auto y = PackUtils::PackUNorm(byteMax, vectorY) << 8;
		const auto z = PackUtils::PackUNorm(byteMax, vectorZ);
		const auto w = PackUtils::PackUNorm(byteMax, vectorW) << 24;

		return x | y | z | w;
	}
}