package harfbuzz

import (
	
	
	
	

	
	ot 
	
)

// ported from harfbuzz/src/hb-ot-map.cc, hb-ot-map.hh Copyright © 2009,2010  Red Hat, Inc. 2010,2011,2013  Google, Inc. Behdad Esfahbod

type otMapFeatureFlags uint8

const (
	ffGLOBAL       otMapFeatureFlags = 1 << iota /* Feature applies to all characters; results in no mask allocated for it. */
	ffHasFallback                                /* Has fallback implementation, so include mask bit even if feature not found. */
	ffManualZWNJ                                 /* Don't skip over ZWNJ when matching **context**. */
	ffManualZWJ                                  /* Don't skip over ZWJ when matching **input**. */
	ffGlobalSearch                               /* If feature not found in LangSys, look for it in global feature list and pick one. */
	ffRandom                                     /* Randomly select a glyph from an AlternateSubstFormat1 subtable. */
	ffPerSyllable                                /* Contain lookup application to within syllable. */

	ffNone                otMapFeatureFlags = 0
	ffManualJoiners                         = ffManualZWNJ | ffManualZWJ
	ffGlobalManualJoiners                   = ffGLOBAL | ffManualJoiners
	ffGlobalHasFallback                     = ffGLOBAL | ffHasFallback
)

const (
	otMapMaxBits  = 8
	otMapMaxValue = (1 << otMapMaxBits) - 1
)

type otMapFeature struct {
	tag   tables.Tag
	flags otMapFeatureFlags
}

type featureInfo struct {
	Tag tables.Tag
	// seq           int /* sequence#, used for stable sorting only */
	maxValue     uint32
	flags        otMapFeatureFlags
	defaultValue uint32 /* for non-global features, what should the unset glyphs take */
	stage        [2]int /* GSUB/GPOS */
}

type stageInfo struct {
	pauseFunc pauseFunc
	index     int
}

type otMapBuilder struct {
	tables        *font.Font
	props         SegmentProperties
	stages        [2][]stageInfo
	featureInfos  []featureInfo
	scriptIndex   [2]int
	languageIndex [2]int
	currentStage  [2]int
	chosenScript  [2]tables.Tag
	foundScript   [2]bool
}

func newOtMapBuilder( *font.Font,  SegmentProperties) otMapBuilder {
	var  otMapBuilder

	.tables = 
	.props = 

	/* Fetch script/language indices for GSUB/GPOS.  We need these later to skip
	* features not available in either table and not waste precious bits for them. */
	,  := newOTTagsFromScriptAndLanguage(.Script, .Language)

	.scriptIndex[0], .chosenScript[0], .foundScript[0] = selectScript(&.GSUB.Layout, )
	.languageIndex[0], _ = selectLanguage(&.GSUB.Layout, .scriptIndex[0], )

	.scriptIndex[1], .chosenScript[1], .foundScript[1] = selectScript(&.GPOS.Layout, )
	.languageIndex[1], _ = selectLanguage(&.GPOS.Layout, .scriptIndex[1], )

	return 
}

func ( *otMapBuilder) ( tables.Tag,  otMapFeatureFlags,  uint32) {
	var  featureInfo
	.Tag = 
	.maxValue = 
	.flags = 
	if ( & ffGLOBAL) != 0 {
		.defaultValue = 
	}
	.stage = .currentStage

	.featureInfos = append(.featureInfos, )
}

// Pause functions return true if new glyph indices might have been
// added to the buffer.  This is used to update buffer digest.
type pauseFunc func(plan *otShapePlan, font *Font, buffer *Buffer) bool

func ( *otMapBuilder) ( int,  pauseFunc) {
	 := stageInfo{
		index:     .currentStage[],
		pauseFunc: ,
	}
	.stages[] = append(.stages[], )
	.currentStage[]++
}

func ( *otMapBuilder) ( pauseFunc) { .addPause(0, ) }
func ( *otMapBuilder) ( pauseFunc) { .addPause(1, ) }

func ( *otMapBuilder) ( tables.Tag,  otMapFeatureFlags,  uint32) {
	.addFeatureExt(, ffGLOBAL|, )
}

// shortand for enableFeatureExt(tag, None, 1)
func ( *otMapBuilder) ( tables.Tag)  { .enableFeatureExt(, ffNone, 1) }
func ( *otMapBuilder) ( tables.Tag)     { .addFeatureExt(, ffNone, 1) }
func ( *otMapBuilder) ( tables.Tag) { .addFeatureExt(, ffGLOBAL, 0) }

func ( *otMapBuilder) ( *otMap,  otShapePlanKey) {
	const  = 8*4 - 1
	const  = 1 << 

	.globalMask = 

	var (
		 [2]uint16 // HB_OT_LAYOUT_NO_FEATURE_INDEX for empty
		   [2]tables.Tag
		/* We default to applying required feature in stage 0. If the required
		* feature has a tag that is known to the shaper, we apply the required feature
		* in the stage for that tag. */
		 [2]int
	)

	,  := .tables.GSUB, .tables.GPOS
	 := [2]*font.Layout{&.Layout, &.Layout}

	.chosenScript = .chosenScript
	.foundScript = .foundScript
	[0], [0] = getRequiredFeature([0], .scriptIndex[0], .languageIndex[0])
	[1], [1] = getRequiredFeature([1], .scriptIndex[1], .languageIndex[1])

	// sort features and merge duplicates
	if len(.featureInfos) != 0 {
		sort.SliceStable(.featureInfos, func(,  int) bool {
			return .featureInfos[].Tag < .featureInfos[].Tag
		})
		 := 0
		for ,  := range .featureInfos {
			if  == 0 {
				continue
			}
			if .Tag != .featureInfos[].Tag {
				++
				.featureInfos[] = 
				continue
			}
			if .flags&ffGLOBAL != 0 {
				.featureInfos[].flags |= ffGLOBAL
				.featureInfos[].maxValue = .maxValue
				.featureInfos[].defaultValue = .defaultValue
			} else {
				if .featureInfos[].flags&ffGLOBAL != 0 {
					.featureInfos[].flags ^= ffGLOBAL
				}
				.featureInfos[].maxValue = max32(.featureInfos[].maxValue, .maxValue)
				// inherit default_value from j
			}
			.featureInfos[].flags |= (.flags & ffHasFallback)
			.featureInfos[].stage[0] = min(.featureInfos[].stage[0], .stage[0])
			.featureInfos[].stage[1] = min(.featureInfos[].stage[1], .stage[1])
		}
		.featureInfos = .featureInfos[0 : +1]
	}

	// allocate bits now
	 := bits.OnesCount32(glyphFlagDefined) + 1

	for ,  := range .featureInfos {

		 := 0

		if (.flags&ffGLOBAL) != 0 && .maxValue == 1 {
			// uses the global bit
			 = 0
		} else {
			// limit bits per feature.
			 = min(otMapMaxBits, bitStorage(.maxValue))
		}

		if .maxValue == 0 || + >=  {
			continue // feature disabled, or not enough bits.
		}

		var (
			        = false
			 [2]uint16
		)
		for ,  := range  {
			if [] == .Tag {
				[] = .stage[]
			}
			[] = findFeatureForLang(, .scriptIndex[], .languageIndex[], .Tag)
			 =  || [] != NoFeatureIndex
		}
		if ! && (.flags&ffGlobalSearch) != 0 {
			for ,  := range  {
				[] = findFeature(, .Tag)
				 =  || [] != NoFeatureIndex
			}
		}
		if ! && .flags&ffHasFallback == 0 {
			continue
		}

		var  featureMap
		.tag = .Tag
		.index = 
		.stage = .stage
		.autoZWNJ = .flags&ffManualZWNJ == 0
		.autoZWJ = .flags&ffManualZWJ == 0
		.random = .flags&ffRandom != 0
		.perSyllable = .flags&ffPerSyllable != 0
		if (.flags&ffGLOBAL) != 0 && .maxValue == 1 {
			// uses the global bit
			.shift = 
			.mask = 
		} else {
			.shift = 
			.mask = (1 << ( + )) - (1 << )
			 += 
			.globalMask |= (.defaultValue << .shift) & .mask
		}
		.mask1 = (1 << .shift) & .mask
		.needsFallback = !

		if debugMode {
			fmt.Printf("\tMAP - adding feature %s (%d) for stage %v\n", .Tag, .Tag, .stage)
		}

		.features = append(.features, )
	}
	.featureInfos = .featureInfos[:0] // done with these

	.addGSUBPause(nil)
	.addGPOSPause(nil)

	// collect lookup indices for features
	for ,  := range  {
		// Collect lookup indices for features
		 := 0
		 := 0
		for  := 0;  < .currentStage[]; ++ {
			if [] != NoFeatureIndex &&
				[] ==  {
				const  = 0x20202020 // ("    ")
				.addLookups(, , [],
					[], , true, true, false, false, )
			}

			for ,  := range .features {
				if .stage[] ==  {
					.addLookups(, ,
						.index[],
						[],
						.mask,
						.autoZWNJ,
						.autoZWJ,
						.random,
						.perSyllable,
						.tag,
					)
				}
			}
			// sort lookups and merge duplicates

			if  := .lookups[];  < len() {
				 := [:]
				sort.Slice(, func(,  int) bool { return [].index < [].index })

				 := 
				for  :=  + 1;  < len(); ++ {
					if [].index != [].index {
						++
						[] = []
					} else {
						[].mask |= [].mask
						[].autoZWNJ = [].autoZWNJ && [].autoZWNJ
						[].autoZWJ = [].autoZWJ && [].autoZWJ
					}
				}
				.lookups[] = [:+1]
			}

			 = len(.lookups[])

			if  < len(.stages[]) && .stages[][].index ==  {
				 := stageMap{
					lastLookup: ,
					pauseFunc:  .stages[][].pauseFunc,
				}
				.stages[] = append(.stages[], )
				++
			}
		}
	}
}

func ( *otMapBuilder) ( ot.Tag) bool {
	 := [2]*font.Layout{&.tables.GSUB.Layout, &.tables.GPOS.Layout}

	for ,  := range  {
		if findFeatureForLang(, .scriptIndex[], .languageIndex[], ) != NoFeatureIndex {
			return true
		}
	}
	return false
}

type featureMap struct {
	tag           tables.Tag /* should be first for our bsearch to work */
	index         [2]uint16  /* GSUB/GPOS */
	stage         [2]int     /* GSUB/GPOS */
	shift         int
	mask          GlyphMask
	mask1         GlyphMask /* mask for value=1, for quick access */
	needsFallback bool      // = 1;
	autoZWNJ      bool      // = 1;
	autoZWJ       bool      // = 1;
	random        bool      // = 1;
	perSyllable   bool
}

// by tag
func bsearchFeature( []featureMap,  tables.Tag) *featureMap {
	,  := 0, len()
	for  <  {
		 :=  + (-)/2 // avoid overflow when computing mid
		 := [].tag
		if  <  {
			 = 
		} else if  >  {
			 =  + 1
		} else {
			return &[]
		}
	}
	return nil
}

type lookupMap struct {
	index       uint16
	autoZWNJ    bool // = 1;
	autoZWJ     bool // = 1;
	random      bool // = 1;
	perSyllable bool
	featureTag  ot.Tag
	mask        GlyphMask

	// HB_INTERNAL static int cmp (const void *pa, const void *pb)
	// {
	//   const lookup_map_t *a = (const lookup_map_t *) pa;
	//   const lookup_map_t *b = (const lookup_map_t *) pb;
	//   return a.index < b.index ? -1 : a.index > b.index ? 1 : 0;
	// }
}

type stageMap struct {
	pauseFunc  pauseFunc
	lastLookup int
}

type otMap struct {
	lookups      [2][]lookupMap
	stages       [2][]stageMap
	features     []featureMap // sorted
	chosenScript [2]tables.Tag
	globalMask   GlyphMask
	foundScript  [2]bool

	applyContext otApplyContext // buffer
}

func ( *otMap) ( tables.Tag) bool {
	if  := bsearchFeature(.features, );  != nil {
		return .needsFallback
	}
	return false
}

func ( *otMap) ( tables.Tag) (GlyphMask, int) {
	if  := bsearchFeature(.features, );  != nil {
		return .mask, .shift
	}
	return 0, 0
}

func ( *otMap) ( tables.Tag) GlyphMask {
	if  := bsearchFeature(.features, );  != nil {
		return .mask1
	}
	return 0
}

func ( *otMap) ( int,  tables.Tag) uint16 {
	if  := bsearchFeature(.features, );  != nil {
		return .index[]
	}
	return NoFeatureIndex
}

func ( *otMap) ( int,  tables.Tag) int {
	if  := bsearchFeature(.features, );  != nil {
		return .stage[]
	}
	return math.MaxInt32
}

func ( *otMap) (,  int) []lookupMap {
	if  > len(.stages[]) {
		return nil
	}
	,  := 0, len(.lookups[])
	if  != 0 {
		 = .stages[][-1].lastLookup
	}
	if  < len(.stages[]) {
		 = .stages[][].lastLookup
	}
	return .lookups[][:]
}

func ( *otMap) ( *font.Layout,  int,  uint16,  int,
	 GlyphMask, , , ,  bool,  ot.Tag,
) {
	 := getFeatureLookupsWithVar(, , )
	for ,  := range  {
		 := lookupMap{
			mask:        ,
			index:       ,
			autoZWNJ:    ,
			autoZWJ:     ,
			random:      ,
			perSyllable: ,
			featureTag:  ,
		}
		.lookups[] = append(.lookups[], )
	}
}

// apply the GSUB table
func ( *otMap) ( *otShapePlan,  *Font,  *Buffer) {
	if debugMode {
		fmt.Println("SUBSTITUTE - start table GSUB")
	}

	 := otProxy{otProxyMeta: proxyGSUB, accels: .gsubAccels}
	.apply(, , , )

	if debugMode {
		fmt.Println("SUBSTITUTE - end table GSUB")
	}
}

// apply the GPOS table
func ( *otMap) ( *otShapePlan,  *Font,  *Buffer) {
	if debugMode {
		fmt.Println("POSITION - start table GPOS")
	}

	 := otProxy{otProxyMeta: proxyGPOS, accels: .gposAccels}
	.apply(, , , )

	if debugMode {
		fmt.Println("POSITION - end table GPOS")
	}
}