// 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.

package language

import 

type scriptRegionFlags uint8

const (
	isList = 1 << iota
	scriptInFrom
	regionInFrom
)

func ( *Tag) ( Language) {
	if .LangID == 0 {
		.LangID = 
	}
}

func ( *Tag) ( Script) {
	if .ScriptID == 0 {
		.ScriptID = 
	}
}

func ( *Tag) ( Region) {
	if .RegionID == 0 || .RegionID.Contains() {
		.RegionID = 
	}
}

// ErrMissingLikelyTagsData indicates no information was available
// to compute likely values of missing tags.
var ErrMissingLikelyTagsData = errors.New("missing likely tags data")

// addLikelySubtags sets subtags to their most likely value, given the locale.
// In most cases this means setting fields for unknown values, but in some
// cases it may alter a value.  It returns an ErrMissingLikelyTagsData error
// if the given locale cannot be expanded.
func ( Tag) () (Tag, error) {
	,  := addTags()
	if  != nil {
		return , 
	} else if .equalTags() {
		return , nil
	}
	.RemakeString()
	return , nil
}

// specializeRegion attempts to specialize a group region.
func specializeRegion( *Tag) bool {
	if  := regionInclusion[.RegionID];  < nRegionGroups {
		 := likelyRegionGroup[]
		if Language(.lang) == .LangID && Script(.script) == .ScriptID {
			.RegionID = Region(.region)
		}
		return true
	}
	return false
}

// Maximize returns a new tag with missing tags filled in.
func ( Tag) () (Tag, error) {
	return addTags()
}

func addTags( Tag) (Tag, error) {
	// We leave private use identifiers alone.
	if .IsPrivateUse() {
		return , nil
	}
	if .ScriptID != 0 && .RegionID != 0 {
		if .LangID != 0 {
			// already fully specified
			specializeRegion(&)
			return , nil
		}
		// Search matches for und-script-region. Note that for these cases
		// region will never be a group so there is no need to check for this.
		 := likelyRegion[.RegionID : .RegionID+1]
		if  := [0]; .flags&isList != 0 {
			 = likelyRegionList[.lang : .lang+uint16(.script)]
		}
		for ,  := range  {
			// Deviating from the spec. See match_test.go for details.
			if Script(.script) == .ScriptID {
				.setUndefinedLang(Language(.lang))
				return , nil
			}
		}
	}
	if .LangID != 0 {
		// Search matches for lang-script and lang-region, where lang != und.
		if .LangID < langNoIndexOffset {
			 := likelyLang[.LangID]
			if .flags&isList != 0 {
				 := likelyLangList[.region : .region+uint16(.script)]
				if .ScriptID != 0 {
					for ,  := range  {
						if Script(.script) == .ScriptID && .flags&scriptInFrom != 0 {
							.setUndefinedRegion(Region(.region))
							return , nil
						}
					}
				} else if .RegionID != 0 {
					 := 0
					 := true
					 := 
					for ,  := range  {
						// We visit all entries for which the script was not
						// defined, including the ones where the region was not
						// defined. This allows for proper disambiguation within
						// regions.
						if .flags&scriptInFrom == 0 && .RegionID.Contains(Region(.region)) {
							.RegionID = Region(.region)
							.setUndefinedScript(Script(.script))
							 =  && .ScriptID == Script(.script)
							++
						}
					}
					if  == 1 {
						return , nil
					}
					// Even if we fail to find a unique Region, we might have
					// an unambiguous script.
					if  {
						.ScriptID = .ScriptID
					}
				}
			}
		}
	} else {
		// Search matches for und-script.
		if .ScriptID != 0 {
			 := likelyScript[.ScriptID]
			if .region != 0 {
				.setUndefinedRegion(Region(.region))
				.setUndefinedLang(Language(.lang))
				return , nil
			}
		}
		// Search matches for und-region. If und-script-region exists, it would
		// have been found earlier.
		if .RegionID != 0 {
			if  := regionInclusion[.RegionID];  < nRegionGroups {
				 := likelyRegionGroup[]
				if .region != 0 {
					.setUndefinedLang(Language(.lang))
					.setUndefinedScript(Script(.script))
					.RegionID = Region(.region)
				}
			} else {
				 := likelyRegion[.RegionID]
				if .flags&isList != 0 {
					 = likelyRegionList[.lang]
				}
				if .script != 0 && .flags != scriptInFrom {
					.setUndefinedLang(Language(.lang))
					.setUndefinedScript(Script(.script))
					return , nil
				}
			}
		}
	}

	// Search matches for lang.
	if .LangID < langNoIndexOffset {
		 := likelyLang[.LangID]
		if .flags&isList != 0 {
			 = likelyLangList[.region]
		}
		if .region != 0 {
			.setUndefinedScript(Script(.script))
			.setUndefinedRegion(Region(.region))
		}
		specializeRegion(&)
		if .LangID == 0 {
			.LangID = _en // default language
		}
		return , nil
	}
	return , ErrMissingLikelyTagsData
}

func ( *Tag) ( Tag) {
	.LangID = .LangID
	.ScriptID = .ScriptID
	.RegionID = .RegionID
}

// minimize removes the region or script subtags from t such that
// t.addLikelySubtags() == t.minimize().addLikelySubtags().
func ( Tag) () (Tag, error) {
	,  := minimizeTags()
	if  != nil {
		return , 
	}
	.RemakeString()
	return , nil
}

// minimizeTags mimics the behavior of the ICU 51 C implementation.
func minimizeTags( Tag) (Tag, error) {
	if .equalTags(Und) {
		return , nil
	}
	,  := addTags()
	if  != nil {
		return , 
	}
	for ,  := range [...]Tag{
		{LangID: .LangID},
		{LangID: .LangID, RegionID: .RegionID},
		{LangID: .LangID, ScriptID: .ScriptID},
	} {
		if ,  := addTags();  == nil && .equalTags() {
			.setTagsFrom()
			break
		}
	}
	return , nil
}