package helper

import (
	"image/color"
	"math"
	"slices"
)

func TestDistance[T tester[T], C color.Color](t T, alpha bool, midpoint func(c0, c1 C) C, f func(c0, c1 C) float64, m color.Model) {
	colors := slices.Collect(EnumColor[C](alpha, false, m))

	for i, c0 := range colors {
		// a colour should have a distance of zero to itself.
		if d := f(c0, c0); !EqFloat64Fuzzy(d, 0) {
			t.Errorf("Distance(%#+v, %#+v) = %f, want 0", c0, c0, d)
			return
		}

		for j := i + 1; j < len(colors); j++ {
			c1 := colors[j]
			d, d2 := f(c0, c1), f(c1, c0)

			switch {
			case math.IsNaN(d) || math.IsInf(d, 0):
				t.Errorf("Distance(%#+v, %#+v) = %f, want finite", c0, c1, d)
				return

			case d < 0 || EqFloat64Fuzzy(d, 0):
				t.Errorf("Distance(%#+v, %#+v) = %f, want > 0", c0, c1, d)
				return

			case !EqFloat64Fuzzy(d, d2):
				t.Errorf("Distance(%#+v, %#+v) != Distance(%#+v, %#+v), want %f == %f", c1, c0, c0, c1, d, d2)
				return
			}

			// traveling from c0 to c1 via mid can't possibly be
			// shorter than traveling from c0 to c1 directly.
			mid := midpoint(c0, c1)
			if cumulative := f(c0, mid) + f(mid, c1); !(d < cumulative || EqFloat64Fuzzy(d, cumulative)) {
				t.Errorf("Distance(%#+v, %#+v)+Distance(%#+v, %#+v) < Distance(%#+v, %#+v), want %f >= %f", c0, mid, mid, c1, c0, c1, cumulative, d)
				return
			}
		}
	}
}