// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

//go:generate go run gen.go gen_index.go -output tables.go
//go:generate go run gen_parents.go

package compact

// TODO: Remove above NOTE after:
// - verifying that tables are dropped correctly (most notably matcher tables).

import (
	

	
)

// Tag represents a BCP 47 language tag. It is used to specify an instance of a
// specific language or locale. All language tag values are guaranteed to be
// well-formed.
type Tag struct {
	// NOTE: exported tags will become part of the public API.
	language ID
	locale   ID
	full     fullTag // always a language.Tag for now.
}

const _und = 0

type fullTag interface {
	IsRoot() bool
	Parent() language.Tag
}

// Make a compact Tag from a fully specified internal language Tag.
func ( language.Tag) ( Tag) {
	if  := .TypeForKey("rg"); len() == 6 && [2:] == "zzzz" {
		if ,  := language.ParseRegion([:2]);  == nil {
			 := 
			, _ = .SetTypeForKey("rg", "")
			// TODO: should we not consider "va" for the language tag?
			var ,  bool
			.language,  = FromTag()
			.RegionID = 
			.locale,  = FromTag()
			if ! || ! {
				.full = 
			}
			return 
		}
	}
	,  := FromTag()
	.language = 
	.locale = 
	if ! {
		.full = 
	}
	return 
}

// Tag returns an internal language Tag version of this tag.
func ( Tag) () language.Tag {
	if .full != nil {
		return .full.(language.Tag)
	}
	 := .language.Tag()
	if .language != .locale {
		 := .locale.Tag()
		, _ = .SetTypeForKey("rg", strings.ToLower(.RegionID.String())+"zzzz")
	}
	return 
}

// IsCompact reports whether this tag is fully defined in terms of ID.
func ( *Tag) () bool {
	return .full == nil
}

// MayHaveVariants reports whether a tag may have variants. If it returns false
// it is guaranteed the tag does not have variants.
func ( Tag) () bool {
	return .full != nil || int(.language) >= len(coreTags)
}

// MayHaveExtensions reports whether a tag may have extensions. If it returns
// false it is guaranteed the tag does not have them.
func ( Tag) () bool {
	return .full != nil ||
		int(.language) >= len(coreTags) ||
		.language != .locale
}

// IsRoot returns true if t is equal to language "und".
func ( Tag) () bool {
	if .full != nil {
		return .full.IsRoot()
	}
	return .language == _und
}

// Parent returns the CLDR parent of t. In CLDR, missing fields in data for a
// specific language are substituted with fields from the parent language.
// The parent for a language may change for newer versions of CLDR.
func ( Tag) () Tag {
	if .full != nil {
		return Make(.full.Parent())
	}
	if .language != .locale {
		// Simulate stripping -u-rg-xxxxxx
		return Tag{language: .language, locale: .language}
	}
	// TODO: use parent lookup table once cycle from internal package is
	// removed. Probably by internalizing the table and declaring this fast
	// enough.
	// lang := compactID(internal.Parent(uint16(t.language)))
	,  := FromTag(.language.Tag().Parent())
	return Tag{language: , locale: }
}

// nextToken returns token t and the rest of the string.
func nextToken( string) (,  string) {
	 := strings.Index([1:], "-")
	if  == -1 {
		return [1:], ""
	}
	++
	return [1:], [:]
}

// LanguageID returns an index, where 0 <= index < NumCompactTags, for tags
// for which data exists in the text repository.The index will change over time
// and should not be stored in persistent storage. If t does not match a compact
// index, exact will be false and the compact index will be returned for the
// first match after repeatedly taking the Parent of t.
func ( Tag) ( ID,  bool) {
	return .language, .full == nil
}

// RegionalID returns the ID for the regional variant of this tag. This index is
// used to indicate region-specific overrides, such as default currency, default
// calendar and week data, default time cycle, and default measurement system
// and unit preferences.
//
// For instance, the tag en-GB-u-rg-uszzzz specifies British English with US
// settings for currency, number formatting, etc. The CompactIndex for this tag
// will be that for en-GB, while the RegionalID will be the one corresponding to
// en-US.
func ( Tag) ( ID,  bool) {
	return .locale, .full == nil
}

// LanguageTag returns t stripped of regional variant indicators.
//
// At the moment this means it is stripped of a regional and variant subtag "rg"
// and "va" in the "u" extension.
func ( Tag) () Tag {
	if .full == nil {
		return Tag{language: .language, locale: .language}
	}
	 := .Tag()
	.SetTypeForKey("rg", "")
	.SetTypeForKey("va", "")
	return Make()
}

// RegionalTag returns the regional variant of the tag.
//
// At the moment this means that the region is set from the regional subtag
// "rg" in the "u" extension.
func ( Tag) () Tag {
	 := Tag{language: .locale, locale: .locale}
	if .full == nil {
		return 
	}
	 := language.Builder{}
	 := .Tag()
	// tag, _ = tag.SetTypeForKey("rg", "")
	.SetTag(.locale.Tag())
	if  := .Variants();  != "" {
		for ,  := range strings.Split(, "-") {
			.AddVariant()
		}
	}
	for ,  := range .Extensions() {
		.AddExt()
	}
	return 
}

// FromTag reports closest matching ID for an internal language Tag.
func ( language.Tag) ( ID,  bool) {
	// TODO: perhaps give more frequent tags a lower index.
	// TODO: we could make the indexes stable. This will excluded some
	//       possibilities for optimization, so don't do this quite yet.
	 = true

	, ,  := .Raw()
	if .HasString() {
		if .IsPrivateUse() {
			// We have no entries for user-defined tags.
			return 0, false
		}
		 := false
		if .HasVariants() {
			if .HasExtensions() {
				 := language.Builder{}
				.SetTag(language.Tag{LangID: , ScriptID: , RegionID: })
				.AddVariant(.Variants())
				 = false
				 = .Make()
			}
			 = true
		} else if ,  := .Extension('u');  {
			// TODO: va may mean something else. Consider not considering it.
			// Strip all but the 'va' entry.
			 := 
			 := .TypeForKey("va")
			 = language.Tag{LangID: , ScriptID: , RegionID: }
			if  != "" {
				, _ = .SetTypeForKey("va", )
				 = true
			}
			 =  == 
		} else {
			 = false
		}
		if  {
			// We have some variants.
			for ,  := range specialTags {
				if  ==  {
					return ID( + len(coreTags)), 
				}
			}
			 = false
		}
	}
	if ,  := getCoreIndex();  {
		return , 
	}
	 = false
	if  != 0 &&  == 0 {
		// Deal with cases where an extra script is inserted for the region.
		,  := .Maximize()
		if ,  := getCoreIndex();  {
			return , 
		}
	}
	for  = .Parent();  != root;  = .Parent() {
		// No variants specified: just compare core components.
		// The key has the form lllssrrr, where l, s, and r are nibbles for
		// respectively the langID, scriptID, and regionID.
		if ,  := getCoreIndex();  {
			return , 
		}
	}
	return 0, 
}

var root = language.Tag{}