package ut

import (
	"errors"
	"strings"
	"sync"
)

var utrans *UniversalTranslator
var once sync.Once

type translators map[string]*Translator

// UniversalTranslator holds all locale Translator instances
type UniversalTranslator struct {
	translators          translators
	translatorsLowercase translators
	fallback             *Translator
}

// newUniversalTranslator creates a new UniversalTranslator instance.
func newUniversalTranslator() *UniversalTranslator {
	return &UniversalTranslator{
		translators:          make(translators),
		translatorsLowercase: make(translators),
	}
}

// SetFallback registers the fallback Translator instance when no matching Translator
// can be found via a given locale
func SetFallback(translator *Translator) {
	utrans.fallback = translator
}

// GetFallback return the universal translators fallback translation
func GetFallback() *Translator {
	return utrans.fallback
}

// FindTranslator trys to find a Translator based on an array of locales
// and returns the first one it can find, otherwise returns the
// fallback translator; the lowercase bool specifies whether to lookup
// the locale by proper or lowercase name, why because the http
// Accept-Language header passed by some browsers has the locale lowercased
// and others proper name so this just makes it easier to lowercase, pass in
// the lowecased array and lookup by lowercase name.
func FindTranslator(locales []string, lowercase bool) *Translator {

	if lowercase {
		for _, locale := range locales {

			if t, ok := utrans.translatorsLowercase[locale]; ok {
				return t
			}
		}
	} else {

		for _, locale := range locales {

			if t, ok := utrans.translators[locale]; ok {
				return t
			}
		}
	}

	return utrans.fallback
}

// GetTranslator returns the specified translator for the given locale,
// or error if not found
func GetTranslator(locale string) (*Translator, error) {

	if t, ok := utrans.translators[locale]; ok {
		return t, nil
	}

	return nil, errors.New("Translator with locale '" + locale + "' counld not be found.")
}

// RegisterLocale registers the a locale with ut
// initializes singleton + sets initial fallback language
func RegisterLocale(loc *Locale) {
	once.Do(func() {
		utrans = newUniversalTranslator()
	})

	if _, ok := utrans.translators[loc.Locale]; ok {
		return
	}

	t := newTranslator(loc)

	if utrans.fallback == nil {
		utrans.fallback = t
	}

	utrans.translators[loc.Locale] = t
	utrans.translatorsLowercase[strings.ToLower(loc.Locale)] = t

	// have do once initialize singleton ut instance.
}
