// Largely inspired by the descriptions in http://lab.medialab.sciences-po.fr/iwanthue/
// but written from scratch.

package colorful

import (
	
	
	
)

// The algorithm works in L*a*b* color space and converts to RGB in the end.
// L* in [0..1], a* and b* in [-1..1]
type lab_t struct {
	L, A, B float64
}

type SoftPaletteSettings struct {
	// A function which can be used to restrict the allowed color-space.
	CheckColor func(l, a, b float64) bool

	// The higher, the better quality but the slower. Usually two figures.
	Iterations int

	// Use up to 160000 or 8000 samples of the L*a*b* space (and thus calls to CheckColor).
	// Set this to true only if your CheckColor shapes the Lab space weirdly.
	ManySamples bool
}

// Yeah, windows-stype Foo, FooEx, screw you golang...
// Uses K-means to cluster the color-space and return the means of the clusters
// as a new palette of distinctive colors. Falls back to K-medoid if the mean
// happens to fall outside of the color-space, which can only happen if you
// specify a CheckColor function.
func ( int,  SoftPaletteSettings) ([]Color, error) {

	// Checks whether it's a valid RGB and also fulfills the potentially provided constraint.
	 := func( lab_t) bool {
		 := Lab(.L, .A, .B)
		return .IsValid() && (.CheckColor == nil || .CheckColor(.L, .A, .B))
	}

	// Sample the color space. These will be the points k-means is run on.
	 := 0.05
	 := 0.1
	if .ManySamples {
		 = 0.01
		 = 0.05
	}

	 := make([]lab_t, 0, int(1.0/*2.0/*2.0/))
	for  := 0.0;  <= 1.0;  +=  {
		for  := -1.0;  <= 1.0;  +=  {
			for  := -1.0;  <= 1.0;  +=  {
				if (lab_t{, , }) {
					 = append(, lab_t{, , })
				}
			}
		}
	}

	// That would cause some infinite loops down there...
	if len() <  {
		return nil, fmt.Errorf("palettegen: more colors requested (%v) than samples available (%v). Your requested color count may be wrong, you might want to use many samples or your constraint function makes the valid color space too small", , len())
	} else if len() ==  {
		return labs2cols(), nil // Oops?
	}

	// We take the initial means out of the samples, so they are in fact medoids.
	// This helps us avoid infinite loops or arbitrary cutoffs with too restrictive constraints.
	 := make([]lab_t, )
	for  := 0;  < ; ++ {
		for [] = [rand.Intn(len())]; in(, , []); [] = [rand.Intn(len())] {
		}
	}

	 := make([]int, len())
	 := make([]bool, len())

	// The actual k-means/medoid iterations
	for  := 0;  < .Iterations; ++ {
		// Reassing the samples to clusters, i.e. to their closest mean.
		// By the way, also check if any sample is used as a medoid and if so, mark that.
		for ,  := range  {
			[] = false
			 := math.Inf(+1)
			for ,  := range  {
				 := lab_dist(, )
				if  <  {
					 = 
					[] = 
				}

				// Mark samples which are used as a medoid.
				if lab_eq(, ) {
					[] = true
				}
			}
		}

		// Compute new means according to the samples.
		for  := range  {
			// The new mean is the average of all samples belonging to it..
			 := 0
			 := lab_t{0.0, 0.0, 0.0}
			for ,  := range  {
				if [] ==  {
					++
					.L += .L
					.A += .A
					.B += .B
				}
			}
			if  > 0 {
				.L /= float64()
				.A /= float64()
				.B /= float64()
			} else {
				// That mean doesn't have any samples? Get a new mean from the sample list!
				var  int
				for  = rand.Intn(len()); [];  = rand.Intn(len()) {
				}
				 = []
				[] = true
			}

			// But now we still need to check whether the new mean is an allowed color.
			if  > 0 && () {
				// It does, life's good (TM)
				[] = 
			} else {
				// New mean isn't an allowed color or doesn't have any samples!
				// Switch to medoid mode and pick the closest (unused) sample.
				// This should always find something thanks to len(samples) >= colorsCount
				 := math.Inf(+1)
				for ,  := range  {
					if ![] {
						 := lab_dist(, )
						if  <  {
							 = 
							 = 
						}
					}
				}
			}
		}
	}
	return labs2cols(), nil
}

// A wrapper which uses common parameters.
func ( int) ([]Color, error) {
	return SoftPaletteEx(, SoftPaletteSettings{nil, 50, false})
}

func in( []lab_t,  int,  lab_t) bool {
	for  := 0;  <  &&  < len(); ++ {
		if [] ==  {
			return true
		}
	}
	return false
}

const LAB_DELTA = 1e-6

func lab_eq(,  lab_t) bool {
	return math.Abs(.L-.L) < LAB_DELTA &&
		math.Abs(.A-.A) < LAB_DELTA &&
		math.Abs(.B-.B) < LAB_DELTA
}

// That's faster than using colorful's DistanceLab since we would have to
// convert back and forth for that. Here is no conversion.
func lab_dist(,  lab_t) float64 {
	return math.Sqrt(sq(.L-.L) + sq(.A-.A) + sq(.B-.B))
}

func labs2cols( []lab_t) ( []Color) {
	 = make([]Color, len())
	for ,  := range  {
		[] = Lab(.L, .A, .B)
	}
	return 
}