package fic

import (
	"bytes"
	"crypto/md5"
	"regexp"
	"strings"
)

// ToURLid converts the given string to a valid URLid.
func ToURLid(str string) string {
	re_a := regexp.MustCompile("[áàâäā]")
	str = re_a.ReplaceAllLiteralString(str, "a")
	re_A := regexp.MustCompile("[ÀÁÂÄĀ]")
	str = re_A.ReplaceAllLiteralString(str, "A")

	re_c := regexp.MustCompile("[ç]")
	str = re_c.ReplaceAllLiteralString(str, "c")
	re_C := regexp.MustCompile("[Ç]")
	str = re_C.ReplaceAllLiteralString(str, "C")

	re_e := regexp.MustCompile("[éèêëȩē]")
	str = re_e.ReplaceAllLiteralString(str, "e")
	re_E := regexp.MustCompile("[ÉÈÊËĒ]")
	str = re_E.ReplaceAllLiteralString(str, "E")

	re_i := regexp.MustCompile("[íìîïī]")
	str = re_i.ReplaceAllLiteralString(str, "i")
	re_I := regexp.MustCompile("[ÌÍÎÏĪ]")
	str = re_I.ReplaceAllLiteralString(str, "I")

	re_l := regexp.MustCompile("[ł]")
	str = re_l.ReplaceAllLiteralString(str, "l")

	re_o := regexp.MustCompile("[òóôöō]")
	str = re_o.ReplaceAllLiteralString(str, "o")
	re_O := regexp.MustCompile("[ÒÓÔÖŌ]")
	str = re_O.ReplaceAllLiteralString(str, "O")

	re_oe := regexp.MustCompile("[œ]")
	str = re_oe.ReplaceAllLiteralString(str, "oe")
	re_OE := regexp.MustCompile("[Œ]")
	str = re_OE.ReplaceAllLiteralString(str, "OE")

	re_u := regexp.MustCompile("[ùúûüū]")
	str = re_u.ReplaceAllLiteralString(str, "u")
	re_U := regexp.MustCompile("[ÙÚÛÜŪ]")
	str = re_U.ReplaceAllLiteralString(str, "U")

	re_y := regexp.MustCompile("[ỳýŷÿȳ]")
	str = re_y.ReplaceAllLiteralString(str, "y")
	re_Y := regexp.MustCompile("[ỲÝŶŸȲ]")
	str = re_Y.ReplaceAllLiteralString(str, "Y")

	re := regexp.MustCompile("[^a-zA-Z0-9]+")
	return strings.TrimSuffix(re.ReplaceAllLiteralString(str, "-"), "-")
}

// Apr1Md5 computes a usable hash for basic auth in Apache and nginx.
// Function copied from https://github.com/jimstudt/http-authentication/blob/master/basic/md5.go
// as it was not exported in package and comes with other unwanted functions.
// Original source code under MIT.
func Apr1Md5(password string, salt string) string {
	initBin := md5.Sum([]byte(password + salt + password))

	initText := bytes.NewBufferString(password + "$apr1$" + salt)

	for i := len(password); i > 0; i -= 16 {
		lim := i
		if lim > 16 {
			lim = 16
		}
		initText.Write(initBin[0:lim])
	}

	for i := len(password); i > 0; i >>= 1 {
		if (i & 1) == 1 {
			initText.WriteByte(byte(0))
		} else {
			initText.WriteByte(password[0])
		}
	}

	bin := md5.Sum(initText.Bytes())

	n := bytes.NewBuffer([]byte{})

	for i := 0; i < 1000; i++ {
		n.Reset()

		if (i & 1) == 1 {
			n.WriteString(password)
		} else {
			n.Write(bin[:])
		}

		if i%3 != 0 {
			n.WriteString(salt)
		}

		if i%7 != 0 {
			n.WriteString(password)
		}

		if (i & 1) == 1 {
			n.Write(bin[:])
		} else {
			n.WriteString(password)
		}

		bin = md5.Sum(n.Bytes())
	}

	result := bytes.NewBuffer([]byte{})

	fill := func(a byte, b byte, c byte) {
		v := (uint(a) << 16) + (uint(b) << 8) + uint(c)

		for i := 0; i < 4; i++ {
			result.WriteByte("./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"[v&0x3f])
			v >>= 6
		}
	}

	fill(bin[0], bin[6], bin[12])
	fill(bin[1], bin[7], bin[13])
	fill(bin[2], bin[8], bin[14])
	fill(bin[3], bin[9], bin[15])
	fill(bin[4], bin[10], bin[5])
	fill(0, 0, bin[11])

	resultString := string(result.Bytes()[0:22])

	return resultString
}

// Function copied from https://github.com/gerow/go-color/blob/master/color.go
type HSL struct {
	H, S, L float64
}

func hueToRGB(v1, v2, h float64) uint32 {
	if h < 0 {
		h += 1
	}
	if h > 1 {
		h -= 1
	}
	var res float64
	switch {
	case 6*h < 1:
		res = (v1 + (v2-v1)*6*h)
	case 2*h < 1:
		res = v2
	case 3*h < 2:
		res = v1 + (v2-v1)*((2.0/3.0)-h)*6
	default:
		res = v1
	}
	return uint32((res + 1/512.0) * 255)

}

func (c HSL) ToRGB() (rgb uint32) {
	h := c.H
	s := c.S
	l := c.L

	if s == 0 {
		// it's gray
		v := uint32((l + 1/512.0) * 255)
		return v*65536 + v*256 + v
	}

	var v1, v2 float64
	if l < 0.5 {
		v2 = l * (1 + s)
	} else {
		v2 = (l + s) - (s * l)
	}

	v1 = 2*l - v2

	r := hueToRGB(v1, v2, h+(1.0/3.0))
	g := hueToRGB(v1, v2, h)
	b := hueToRGB(v1, v2, h-(1.0/3.0))

	return r*65536 + g*256 + b
}
