import SDL2
import HSLuv


public struct Colour: Equatable { let r, g, b, a: Float }

public extension Colour
{
	static var zero: Self  { .init(r: 0, g: 0, b: 0, a: 0) }
	static var black: Self { .init(r: 0, g: 0, b: 0, a: 1) }
	static var white: Self { .init(r: 1, g: 1, b: 1, a: 1) }

	init(r: Float, g: Float, b: Float)
	{
		self.init(r: r, g: g, b: b, a: 1.0)
	}

	init(fromSdl: SDL_Color)
	{
		self.init(
			r: Float(fromSdl.r) / 0xFF,
			g: Float(fromSdl.g) / 0xFF,
			b: Float(fromSdl.b) / 0xFF,
			a: Float(fromSdl.a) / 0xFF)
	}

	init(fromRgb24: UInt32)
	{
		self.init(
			r: Float((fromRgb24 >> 16) & 0xFF) / 0xFF,
			g: Float((fromRgb24 >>  8) & 0xFF) / 0xFF,
			b: Float((fromRgb24 >>  0) & 0xFF) / 0xFF,
			a: 1)
	}

	init(fromRgba32: UInt32)
	{
		self.init(
			r: Float((fromRgba32 >> 24) & 0xFF) / 0xFF,
			g: Float((fromRgba32 >> 16) & 0xFF) / 0xFF,
			b: Float((fromRgba32 >>  8) & 0xFF) / 0xFF,
			a: Float((fromRgba32 >>  0) & 0xFF) / 0xFF)
	}

	func mix(with rhs: Colour, _ amount: Float) -> Colour
	{
		let x = amount.saturate
		return Colour(
			r: Float.lerp(r, rhs.r, x),
			g: Float.lerp(g, rhs.g, x),
			b: Float.lerp(b, rhs.b, x),
			a: Float.lerp(a, rhs.a, x))
	}

	func lighten(by: Double) -> Colour
	{
		var hue = CGFloat(), sat = CGFloat(), light = CGFloat()
		rgbToHsluv(CGFloat(r), CGFloat(g), CGFloat(b), &hue, &sat, &light)
		let gamma: CGFloat = 3.0
		light = pow((pow(light / 100, 1 / gamma) + CGFloat(by) * 0.8).saturate, gamma) * 100
		var newR = CGFloat(), newG = CGFloat(), newB = CGFloat()
		hsluvToRgb(hue, sat, light, &newR, &newG, &newB)
		return Colour(r: Float(newR), g: Float(newG), b: Float(newB), a: a)
	}

	var linear: Colour { get
	{
		Colour(
			r: linearFromSrgb(r),
			g: linearFromSrgb(g),
			b: linearFromSrgb(b),
			a: a)
	} }

	func setAlpha(_ newAlpha: Float) -> Colour
	{
		return Colour(r: r, g: g, b: b, a: newAlpha)
	}
}


@inline(__always) fileprivate func srgbFromLinear(_ x: Float) -> Float
{
	return (x < 0.0031308)
		? x * 12.92
		: 1.055 * pow(x, 1.0 / 2.4) - 0.055
}

@inline(__always) fileprivate func linearFromSrgb(_ x: Float) -> Float
{
	return (x < 0.04045)
		? x / 12.92
		: pow((x + 0.055) / 1.055, 2.4)
}

