package colors

import (
	"fmt"
	"math"
)

type HSL struct {
	H int // 0-360
	S int // 0-100
	L int // 0-100
}

func (h HSL) String() string {
	return fmt.Sprintf("hsl(%d, %d%%, %d%%)", h.H, h.S, h.L)
}

func (h HSL) ToPrecise() PreciseColor {
	// Normalize H, S, L
	hue := float64(h.H) / 360.0
	sat := float64(h.S) / 100.0
	light := float64(h.L) / 100.0

	var r, g, b float64

	if sat == 0 {
		// Achromatic case
		r, g, b = light, light, light
	} else {
		var q float64
		if light < 0.5 {
			q = light * (1 + sat)
		} else {
			q = light + sat - (light * sat)
		}
		p := 2*light - q
		r = hueToRGB(p, q, hue+1.0/3.0)
		g = hueToRGB(p, q, hue)
		b = hueToRGB(p, q, hue-1.0/3.0)
	}

	return PreciseColor{R: r, G: g, B: b}
}

func hueToRGB(p, q, t float64) float64 {
	if t < 0 {
		t += 1
	}
	if t > 1 {
		t -= 1
	}
	if t < 1.0/6.0 {
		return p + (q-p)*6*t
	}
	if t < 1.0/2.0 {
		return q
	}
	if t < 2.0/3.0 {
		return p + (q-p)*(2.0/3.0-t)*6
	}
	return p
}

func (h HSL) FromPrecise(p PreciseColor) ColorSpace {
	r := p.R
	g := p.G
	b := p.B

	max := math.Max(math.Max(r, g), b)
	min := math.Min(math.Min(r, g), b)
	delta := max - min

	light := (max + min) / 2
	var sat, hue float64

	if delta == 0 {
		// Achromatic case
		hue, sat = 0, 0
	} else {
		if light < 0.5 {
			sat = delta / (max + min)
		} else {
			sat = delta / (2 - max - min)
		}

		switch max {
		case r:
			hue = (g-b)/delta + (6 * boolToFloat64(g < b))
		case g:
			hue = (b-r)/delta + 2
		case b:
			hue = (r-g)/delta + 4
		}
		hue /= 6
		hue = math.Mod(hue, 1)
		if hue < 0 {
			hue += 1
		}
	}

	return HSL{
		H: int(math.Round(hue * 360)),
		S: int(math.Round(sat * 100)),
		L: int(math.Round(light * 100)),
	}
}

func boolToFloat64(b bool) float64 {
	if b {
		return 1
	}
	return 0
}
