package harfbuzz

import (
	

	
	ot 
)

// ported from harfbuzz/src/hb-aat-map.cc, hb-att-map.hh Copyright © 2018  Google, Inc. Behdad Esfahbod

type rangeFlags struct {
	flags        GlyphMask
	clusterFirst int
	clusterLast  int // end - 1
}

type aatMap struct {
	chainFlags [][]rangeFlags
}

func ( *aatMap) ( int) {
	if cap(.chainFlags) >=  {
		.chainFlags = .chainFlags[0:]
	} else {
		.chainFlags = make([][]rangeFlags, )
	}
}

type aatFeatureRange struct {
	info       aatFeatureInfo
	start, end int
}

type aatFeatureEvent struct {
	index   int
	start   bool
	feature aatFeatureInfo
}

func ( aatFeatureEvent) ( aatFeatureEvent) bool {
	if .index < .index {
		return true
	} else if .index > .index {
		return false
	} else {
		if !.start && .start {
			return true
		} else if .start && !.start {
			return false
		}
		return .feature.isLess(.feature)
	}
}

type aatFeatureInfo struct {
	type_       aatLayoutFeatureType
	setting     aatLayoutFeatureSelector
	isExclusive bool
}

func ( aatFeatureInfo) () uint32 {
	return uint32(.type_)<<16 | uint32(.setting)
}

const selMask = ^aatLayoutFeatureSelector(1)

func ( aatFeatureInfo) ( aatFeatureInfo) bool {
	if .type_ != .type_ {
		return .type_ < .type_
	}
	if !.isExclusive && (.setting&selMask) != (.setting&selMask) {
		return .setting < .setting
	}
	return false
}

type aatMapBuilder struct {
	tables *font.Font
	props  SegmentProperties

	features        []aatFeatureRange
	currentFeatures []aatFeatureInfo // sorted by (type_, setting) after compilation
	rangeFirst      int
	rangeLast       int
}

func newAatMapBuilder( *font.Font,  SegmentProperties) aatMapBuilder {
	return aatMapBuilder{
		tables:     ,
		props:      ,
		rangeFirst: FeatureGlobalStart,
		rangeLast:  FeatureGlobalEnd,
	}
}

// binary search into `currentFeatures`, comparing type_ and setting only
func ( *aatMapBuilder) ( aatFeatureInfo) bool {
	 := .key()
	for ,  := 0, len(.currentFeatures);  < ; {
		 :=  + (-)/2
		 := .currentFeatures[].key()
		if  <  {
			 = 
		} else if  <  {
			 =  + 1
		} else {
			return true
		}
	}
	return false
}

func ( *aatMapBuilder) ( *aatMap) {
	 := .tables.Morx
	.resizeChainFlags(len())
	for ,  := range  {
		.chainFlags[] = append(.chainFlags[], rangeFlags{
			flags:        .compileMorxFlag(),
			clusterFirst: .rangeFirst,
			clusterLast:  .rangeLast,
		})
	}

	// TODO: for now we dont support deprecated mort table
	// mort := mapper.face.table.mort
	// if mort.has_data() {
	// 	mort.compile_flags(mapper, map_)
	// 	return
	// }
}

func ( *aatMapBuilder) ( font.MorxChain) GlyphMask {
	 := .DefaultFlags

	for ,  := range .Features {
		,  := .FeatureType, .FeatureSetting

	:
		// Check whether this type_/setting pair was requested in the map, and if so, apply its flags.
		// (The search here only looks at the type_ and setting fields of feature_info_t.)
		 := aatFeatureInfo{, , false}
		if .hasFeature() {
			 &= .DisableFlags
			 |= .EnableFlags
		} else if  == aatLayoutFeatureTypeLetterCase &&  == aatLayoutFeatureSelectorSmallCaps {
			/* Deprecated. https://github.com/harfbuzz/harfbuzz/issues/1342 */
			 = aatLayoutFeatureTypeLowerCase
			 = aatLayoutFeatureSelectorLowerCaseSmallCaps
			goto 
		} else if  == aatLayoutFeatureTypeLanguageTagType &&  != 0 && langMatches(string(.tables.Ltag.Language(-1)), string(.props.Language)) {
			 &= .DisableFlags
			 |= .EnableFlags
		}
	}
	return 
}

func ( *aatMapBuilder) ( Feature) {
	 := .tables.Feat
	if len(.Names) == 0 {
		return
	}

	if .Tag == ot.NewTag('a', 'a', 'l', 't') {
		if  := .GetFeature(aatLayoutFeatureTypeCharacterAlternatives);  == nil || len(.SettingTable) == 0 {
			return
		}
		 := aatFeatureRange{
			info: aatFeatureInfo{
				type_:       aatLayoutFeatureTypeCharacterAlternatives,
				setting:     aatLayoutFeatureSelector(.Value),
				isExclusive: true,
			},
			start: .Start,
			end:   .End,
		}
		.features = append(.features, )
		return
	}

	 := aatLayoutFindFeatureMapping(.Tag)
	if  == nil {
		return
	}

	 := .GetFeature(.aatFeatureType)
	if  == nil || len(.SettingTable) == 0 {
		/* Special case: compileMorxFlag() will fall back to the deprecated version of
		 * small-caps if necessary, so we need to check for that possibility.
		 * https://github.com/harfbuzz/harfbuzz/issues/2307 */
		if .aatFeatureType == aatLayoutFeatureTypeLowerCase &&
			.selectorToEnable == aatLayoutFeatureSelectorLowerCaseSmallCaps {
			 = .GetFeature(aatLayoutFeatureTypeLetterCase)
			if  == nil || len(.SettingTable) == 0 {
				return
			}
		} else {
			return
		}
	}

	var  aatFeatureInfo
	.type_ = .aatFeatureType
	if .Value != 0 {
		.setting = .selectorToEnable
	} else {
		.setting = .selectorToDisable
	}
	.isExclusive = .IsExclusive()
	.features = append(.features, aatFeatureRange{
		info:  ,
		start: .Start,
		end:   .End,
	})
}

func ( *aatMapBuilder) ( *aatMap) {
	// Compute active features per range, and compile each.

	if len(.features) == 0 {
		.compileMap()
		return
	}

	// Sort features by start/end events.
	 := make([]aatFeatureEvent, 0, 2*len(.features)+1)
	for ,  := range .features {
		if .start == .end {
			continue
		}

		 = append(, aatFeatureEvent{
			index:   .start,
			start:   true,
			feature: .info,
		}, aatFeatureEvent{
			index:   .end,
			start:   false,
			feature: .info,
		})
	}
	sort.SliceStable(, func(,  int) bool { return [].isLess([]) })

	// Add a strategic final event.
	{
		 = append(, aatFeatureEvent{
			index:   -1, /* This value does magic. */
			start:   false,
			feature: aatFeatureInfo{},
		})
	}

	// Scan events and save features for each range.
	var  []aatFeatureInfo
	 := 0
	for ,  := range  {
		if .index !=  {
			// Save a snapshot of active features and the range.

			// sort features and merge duplicates
			.currentFeatures = 
			.rangeFirst = 
			.rangeLast = .index - 1
			if len(.currentFeatures) != 0 {
				sort.SliceStable(.currentFeatures, func(,  int) bool {
					return .currentFeatures[].isLess(.currentFeatures[])
				})
				 := 0
				for  := 1;  < len(.currentFeatures); ++ {
					/* Nonexclusive feature selectors come in even/odd pairs to turn a setting on/off
					* respectively, so we mask out the low-order bit when checking for "duplicates"
					* (selectors referring to the same feature setting) here. */
					if .currentFeatures[].type_ != .currentFeatures[].type_ ||
						(!.currentFeatures[].isExclusive && ((.currentFeatures[].setting & selMask) != (.currentFeatures[].setting & selMask))) {
						++
						.currentFeatures[] = .currentFeatures[]
					}
				}
				.currentFeatures = .currentFeatures[:+1]
			}

			.compileMap()

			 = .index
		}

		if .start {
			 = append(, .feature)
		} else {
			for ,  := range  {
				if .key() == .feature.key() {
					// remove the item
					 = append([:], [+1:]...)
					break
				}
			}
		}
	}

	for ,  := range .chainFlags {
		// With our above setup this value is one less than desired; adjust it.
		[len()-1].clusterLast = FeatureGlobalEnd
	}
}