package colorful

import 

// Source: https://github.com/hsluv/hsluv-go
// Under MIT License
// Modified so that Saturation and Luminance are in [0..1] instead of [0..100].

// HSLuv uses a rounded version of the D65. This has no impact on the final RGB
// values, but to keep high levels of accuracy for internal operations and when
// comparing to the test values, this modified white reference is used internally.
//
// See this GitHub thread for details on these values:
//     https://github.com/hsluv/hsluv/issues/79
var hSLuvD65 = [3]float64{0.95045592705167, 1.0, 1.089057750759878}

func (, ,  float64) (float64, float64, float64) {
	// [-1..1] but the code expects it to be [-100..100]
	 *= 100.0
	 *= 100.0

	var ,  float64
	if  > 99.9999999 ||  < 0.00000001 {
		 = 0.0
	} else {
		 = maxChromaForLH(, )
		 =  /  * 100.0
	}
	return , clamp01( / 100.0), clamp01( / 100.0)
}

func (, ,  float64) (float64, float64, float64) {
	 *= 100.0
	 *= 100.0

	var ,  float64
	if  > 99.9999999 ||  < 0.00000001 {
		 = 0.0
	} else {
		 = maxChromaForLH(, )
		 =  / 100.0 * 
	}

	// c is [-100..100], but for LCh it's supposed to be almost [-1..1]
	return clamp01( / 100.0),  / 100.0, 
}

func (, ,  float64) (float64, float64, float64) {
	// [-1..1] but the code expects it to be [-100..100]
	 *= 100.0
	 *= 100.0

	var ,  float64
	if  > 99.9999999 ||  < 0.00000001 {
		 = 0.0
	} else {
		 = maxSafeChromaForL()
		 =  /  * 100.0
	}
	return ,  / 100.0,  / 100.0
}

func (, ,  float64) (float64, float64, float64) {
	// [-1..1] but the code expects it to be [-100..100]
	 *= 100.0
	 *= 100.0

	var ,  float64
	if  > 99.9999999 ||  < 0.00000001 {
		 = 0.0
	} else {
		 = maxSafeChromaForL()
		 =  / 100.0 * 
	}
	return  / 100.0,  / 100.0, 
}

// HSLuv creates a new Color from values in the HSLuv color space.
// Hue in [0..360], a Saturation [0..1], and a Luminance (lightness) in [0..1].
//
// The returned color values are clamped (using .Clamped), so this will never output
// an invalid color.
func (, ,  float64) Color {
	// HSLuv -> LuvLCh -> CIELUV -> CIEXYZ -> Linear RGB -> sRGB
	, ,  := LuvLChToLuv(HSLuvToLuvLCh(, , ))
	return LinearRgb(XyzToLinearRgb(LuvToXyzWhiteRef(, , , hSLuvD65))).Clamped()
}

// HPLuv creates a new Color from values in the HPLuv color space.
// Hue in [0..360], a Saturation [0..1], and a Luminance (lightness) in [0..1].
//
// The returned color values are clamped (using .Clamped), so this will never output
// an invalid color.
func (, ,  float64) Color {
	// HPLuv -> LuvLCh -> CIELUV -> CIEXYZ -> Linear RGB -> sRGB
	, ,  := LuvLChToLuv(HPLuvToLuvLCh(, , ))
	return LinearRgb(XyzToLinearRgb(LuvToXyzWhiteRef(, , , hSLuvD65))).Clamped()
}

// HSLuv returns the Hue, Saturation and Luminance of the color in the HSLuv
// color space. Hue in [0..360], a Saturation [0..1], and a Luminance
// (lightness) in [0..1].
func ( Color) () (, ,  float64) {
	// sRGB -> Linear RGB -> CIEXYZ -> CIELUV -> LuvLCh -> HSLuv
	return LuvLChToHSLuv(.LuvLChWhiteRef(hSLuvD65))
}

// HPLuv returns the Hue, Saturation and Luminance of the color in the HSLuv
// color space. Hue in [0..360], a Saturation [0..1], and a Luminance
// (lightness) in [0..1].
//
// Note that HPLuv can only represent pastel colors, and so the Saturation
// value could be much larger than 1 for colors it can't represent.
func ( Color) () (, ,  float64) {
	return LuvLChToHPLuv(.LuvLChWhiteRef(hSLuvD65))
}

// DistanceHSLuv calculates Euclidan distance in the HSLuv colorspace. No idea
// how useful this is.
//
// The Hue value is divided by 100 before the calculation, so that H, S, and L
// have the same relative ranges.
func ( Color) ( Color) float64 {
	, ,  := .HSLuv()
	, ,  := .HSLuv()
	return math.Sqrt(sq((-)/100.0) + sq(-) + sq(-))
}

// DistanceHPLuv calculates Euclidean distance in the HPLuv colorspace. No idea
// how useful this is.
//
// The Hue value is divided by 100 before the calculation, so that H, S, and L
// have the same relative ranges.
func ( Color) ( Color) float64 {
	, ,  := .HPLuv()
	, ,  := .HPLuv()
	return math.Sqrt(sq((-)/100.0) + sq(-) + sq(-))
}

var m = [3][3]float64{
	{3.2409699419045214, -1.5373831775700935, -0.49861076029300328},
	{-0.96924363628087983, 1.8759675015077207, 0.041555057407175613},
	{0.055630079696993609, -0.20397695888897657, 1.0569715142428786},
}

const kappa = 903.2962962962963
const epsilon = 0.0088564516790356308

func maxChromaForLH(,  float64) float64 {
	 :=  / 360.0 * math.Pi * 2.0
	 := math.MaxFloat64
	for ,  := range getBounds() {
		 := lengthOfRayUntilIntersect(, [0], [1])
		if  > 0.0 &&  <  {
			 = 
		}
	}
	return 
}

func getBounds( float64) [6][2]float64 {
	var  float64
	var  [6][2]float64
	 := math.Pow(+16.0, 3.0) / 1560896.0
	if  > epsilon {
		 = 
	} else {
		 =  / kappa
	}
	for  := range m {
		for  := 0;  < 2; ++ {
			 := (284517.0*m[][0] - 94839.0*m[][2]) * 
			 := (838422.0*m[][2]+769860.0*m[][1]+731718.0*m[][0])** - 769860.0*float64()*
			 := (632260.0*m[][2]-126452.0*m[][1])* + 126452.0*float64()
			[*2+][0] =  / 
			[*2+][1] =  / 
		}
	}
	return 
}

func lengthOfRayUntilIntersect(, ,  float64) ( float64) {
	 =  / (math.Sin() - *math.Cos())
	return
}

func maxSafeChromaForL( float64) float64 {
	 := math.MaxFloat64
	for ,  := range getBounds() {
		 := [0]
		 := [1]
		 := intersectLineLine(, , -1.0/, 0.0)
		 := distanceFromPole(, +*)
		if  <  {
			 = 
		}
	}
	return 
}

func intersectLineLine(, , ,  float64) float64 {
	return ( - ) / ( - )
}

func distanceFromPole(,  float64) float64 {
	return math.Sqrt(math.Pow(, 2.0) + math.Pow(, 2.0))
}