/*
 * @Author: lwnmengjing
 * @Date: 2021/6/9 10:59 上午
 * @Last Modified by: lwnmengjing
 * @Last Modified time: 2021/6/9 10:59 上午
 */

package lang

import (
	"sort"
	"strconv"
	"strings"
)

type language struct {
	name    string
	quality float64
}

type languageSlice []language

func (e languageSlice) SortByQuality() {
	sort.Sort(e)
}

func (e languageSlice) Len() int {
	return len(e)
}

func (e languageSlice) Swap(i, j int) {
	e[i], e[j] = e[j], e[i]
}

func (e languageSlice) Less(i, j int) bool {
	return e[i].quality > e[j].quality
}

// ParseAcceptLanguage returns RFC1766 language codes parsed and sorted from
// languages.
//
// If supportedLanguages is not empty, the returned codes will be filtered
// by its contents.
func ParseAcceptLanguage(languages string, supportedLanguages []string) []string {
	preferredLanguages := strings.Split(languages, ",")
	preferredLanguagesLen := len(preferredLanguages)

	// Preallocate processed languages, as we know the maximum possible.
	langCap := preferredLanguagesLen
	if len(supportedLanguages) > 0 {
		langCap = len(supportedLanguages)
	}
	langs := make(languageSlice, 0, langCap)

	for i, rawPreferredLanguage := range preferredLanguages {
		// Format strings.
		preferredLanguage := strings.Replace(strings.ToLower(strings.TrimSpace(rawPreferredLanguage)), "_", "-", 0)

		if preferredLanguage == "" {
			continue
		}

		// Split out quality factor.
		parts := strings.SplitN(preferredLanguage, ";", 2)

		// If supported languages are given, return only the langs that fit.
		supported := len(supportedLanguages) == 0
		for _, supportedLanguage := range supportedLanguages {
			if supported = supportedLanguage == parts[0]; supported {
				break
			}
		}

		if !supported {
			continue
		}

		lang := language{parts[0], 0}
		if len(parts) == 2 {
			q := parts[1]

			if strings.HasPrefix(q, "q=") {
				q = strings.SplitN(q, "=", 2)[1]
				var err error
				if lang.quality, err = strconv.ParseFloat(q, 64); err != nil {
					// Default value (1) if quality is empty.
					lang.quality = 1
				}
			}
		}

		// Use order of items if no quality is given.
		if lang.quality == 0 {
			lang.quality = float64(preferredLanguagesLen - i)
		}

		langs = append(langs, lang)

	}

	langs.SortByQuality()

	// Filter quality string.
	langString := make([]string, 0, len(langs))
	for _, lang := range langs {
		langString = append(langString, lang.name)
	}

	return langString

}
