package harfbuzz

import (
	

	
	ot 
	
)

// ported from src/hb-ot-layout.cc, hb-ot-layout.hh
// Copyright © 1998-2004  David Turner and Werner Lemberg
// Copyright © 2006  2007,2008,2009  Red Hat, Inc. 2012,2013  Google, Inc. Behdad Esfahbod

const (
	// This bit relates only to the correct processing of
	// the cursive attachment lookup type (GPOS lookup type 3).
	// When this bit is set, the last glyph in a given sequence to
	// which the cursive attachment lookup is applied, will be positioned on the baseline.
	otRightToLeft      uint16 = 1 << iota
	otIgnoreBaseGlyphs        // If set, skips over base glyphs
	otIgnoreLigatures         // If set, skips over ligatures
	otIgnoreMarks             // If set, skips over all combining marks
	// If set, indicates that the lookup table structure
	// is followed by a MarkFilteringSet field.
	// The layout engine skips over all mark glyphs not in the mark filtering set indicated.
	_
	_ uint16 = 0x00E0 // For future use (Set to zero)
	// If not zero, skips over all marks of attachment
	// type different from specified.
	otMarkAttachmentType uint16 = 0xFF00
)

type otLayoutMappingCache = cache15_8_7

const maxNestingLevel = 6

func ( *otApplyContext) ( otProxyMeta,  *otLayoutLookupAccelerator) {
	 := .buffer
	 := .lookup

	if len(.Info) == 0 || .lookupMask == 0 {
		return
	}
	.setLookupProps(.Props())
	if !.isReverse() {
		// in/out forward substitution/positioning
		if !.inplace {
			.clearOutput()
		}
		.idx = 0

		.applyForward()
		if !.inplace {
			.swapBuffers()
		}
	} else {
		/* in-place backward substitution/positioning */
		// assert (!buffer->have_output);

		.idx = len(.Info) - 1

		.applyBackward()
	}
}

func ( *otApplyContext) ( *otLayoutLookupAccelerator) bool {
	 := .buffer
	 := .Info
	 := false
	for {
		 := .idx
		for  < len() &&
			!(.digest.mayHave(gID([].Glyph)) &&
				([].Mask&.lookupMask) != 0 &&
				.checkGlyphProperty(&[], .lookupProps)) {
			++
		}
		if  > .idx {
			.nextGlyphs( - .idx)
		}
		if .idx >= len() {
			break
		}

		 := .apply()
		if  {
			 = true
		} else {
			.nextGlyph()
		}
	}
	return 
}

func ( *otApplyContext) ( *otLayoutLookupAccelerator) bool {
	 := false
	 := .buffer
	for  := true; ;  = .idx >= 0 {
		 := .cur(0)
		if .digest.mayHave(gID(.Glyph)) &&
			(.Mask&.lookupMask != 0) &&
			.checkGlyphProperty(, .lookupProps) {
			 := .apply()
			 =  || 
		}

		// the reverse lookup doesn't "advance" cursor (for good reason).
		.idx--
	}
	return 
}

func ( *otMap) ( otProxy,  *otShapePlan,  *Font,  *Buffer) {
	 := .tableIndex
	 := 0
	 := &.applyContext

	.reset(, , )
	.recurseFunc = .recurseFunc

	for ,  := range .stages[] {

		if debugMode {
			fmt.Printf("\tAPPLY - stage %d\n", )
		}

		for ;  < .lastLookup; ++ {
			 := .lookups[][]
			 := .index

			if debugMode {
				fmt.Printf("\t\tLookup %d start\n", )
			}

			// Only try applying the lookup if there is any overlap.
			 := &.accels[]
			if .digest.mayIntersects(.digest) {
				.lookupIndex = 
				.lookupMask = .mask
				.autoZWJ = .autoZWJ
				.autoZWNJ = .autoZWNJ
				.random = .random
				.perSyllable = .perSyllable

				// pathological cases
				if len(.buffer.Info) > .buffer.maxLen {
					return
				}
				.applyString(.otProxyMeta, )
			}

			if debugMode {
				fmt.Print("\t\tLookup end : ")
				if .tableIndex == 0 {
					fmt.Println(.buffer.Info)
				} else {
					fmt.Println(.buffer.Pos)
				}
			}

		}

		if .pauseFunc != nil {
			if debugMode {
				fmt.Println("\t\tExecuting pause function")
			}

			if .pauseFunc(, , ) {
				// Refresh working buffer digest since buffer changed.
				.updateDigest()
			}
		}
	}
}

/*
 * kern
 */

// tests whether a face includes any state-machine kerning in the 'kern' table.
//
// Does NOT examine the GPOS table.
func hasMachineKerning( font.Kernx) bool {
	for ,  := range  {
		if ,  := .Data.(font.Kern1);  {
			return true
		}
	}
	return false
}

// tests whether a face has any cross-stream kerning (i.e., kerns
// that make adjustments perpendicular to the direction of the text
// flow: Y adjustments in horizontal text or X adjustments in
// vertical text) in the 'kern' table.
//
// Does NOT examine the GPOS table.
func hasCrossKerning( font.Kernx) bool {
	for ,  := range  {
		if .IsCrossStream() {
			return true
		}
	}
	return false
}

func ( *otShapePlan) ( *Font,  *Buffer) {
	 := .face.Kern
	 := newAatApplyContext(, , )
	.applyKernx(, .kernAccels)
}

var otTagLatinScript = ot.NewTag('l', 'a', 't', 'n')

// selectScript selects an OpenType script from the `scriptTags` array,
// returning its index in the Scripts slice and the script tag.
//
// If `table` does not have any of the requested scripts, then `DFLT`,
// `dflt`, and `latn` tags are tried in that order. If the table still does not
// have any of these scripts, NoScriptIndex is returned.
//
// An additional boolean if returned : it is `true` if one of the requested scripts is selected, or `false` if a fallback
// script is selected or if no scripts are selected.
func selectScript( *font.Layout,  []tables.Tag) (int, tables.Tag, bool) {
	for ,  := range  {
		if  := .FindScript();  != -1 {
			return , , true
		}
	}

	// try finding 'DFLT'
	if  := .FindScript(tagDefaultScript);  != -1 {
		return , tagDefaultScript, false
	}

	// try with 'dflt'; MS site has had typos and many fonts use it now :(
	if  := .FindScript(tagDefaultLanguage);  != -1 {
		return , tagDefaultLanguage, false
	}

	// try with 'latn'; some old fonts put their features there even though
	// they're really trying to support Thai, for example :(
	if  := .FindScript(otTagLatinScript);  != -1 {
		return , otTagLatinScript, false
	}

	return NoScriptIndex, NoScriptIndex, false
}

// selectLanguage fetches the index of the first language tag from `languageTags` in the specified layout table,
// underneath `scriptIndex`.
// It not found, the `dflt` language tag is searched.
// Return `true` if the requested language tag is found, `false` otherwise.
// If `scriptIndex` is `NoScriptIndex` or if no language is found, `DefaultLanguageIndex` is returned.
func selectLanguage( *font.Layout,  int,  []tables.Tag) (int, bool) {
	if  == NoScriptIndex {
		return DefaultLanguageIndex, false
	}

	 := .Scripts[]

	for ,  := range  {
		if  := .FindLanguage();  != -1 {
			return , true
		}
	}

	// try finding 'dflt'
	if  := .FindLanguage(tagDefaultLanguage);  != -1 {
		return , false
	}

	return DefaultLanguageIndex, false
}

func findFeature( *font.Layout,  tables.Tag) uint16 {
	if ,  := .FindFeatureIndex();  {
		return 
	}
	return NoFeatureIndex
}

// Fetches the index of a given feature tag in the specified face's GSUB table
// or GPOS table, underneath the specified script and language.
// Return `NoFeatureIndex` it the the feature is not found.
func findFeatureForLang( *font.Layout, ,  int,  tables.Tag) uint16 {
	if  == NoScriptIndex {
		return NoFeatureIndex
	}

	 := .Scripts[].GetLangSys(uint16())
	for ,  := range .FeatureIndices {
		if  == .Features[].Tag {
			return 
		}
	}

	return NoFeatureIndex
}

// Fetches the tag of a requested feature index in the given layout table,
// underneath the specified script and language. Returns -1 if no feature is requested.
func getRequiredFeature( *font.Layout, ,  int) (uint16, tables.Tag) {
	if  == NoScriptIndex {
		return NoFeatureIndex, 0
	}

	 := .Scripts[].GetLangSys(uint16())
	if .RequiredFeatureIndex == 0xFFFF {
		return NoFeatureIndex, 0
	}
	 := .RequiredFeatureIndex
	return , .Features[].Tag
}

// getFeatureLookupsWithVar fetches a list of all lookups enumerated for the specified feature, in
// the given table, enabled at the specified variations index.
// it returns the basic feature if `variationsIndex == noVariationsIndex`
func getFeatureLookupsWithVar( *font.Layout,  uint16,  int) []uint16 {
	if  == NoFeatureIndex {
		return nil
	}

	if  == noVariationsIndex { // just fetch the feature
		return .Features[].LookupListIndices
	}

	// hook the variations
	 := .FeatureVariations[].Substitutions.Substitutions
	for ,  := range  {
		if .FeatureIndex ==  {
			return .AlternateFeature.LookupListIndices
		}
	}
	return nil
}

// tests whether a specified lookup index in the specified face would
// trigger a substitution on the given glyph sequence.
// zeroContext indicating whether substitutions should be context-free.
func otLayoutLookupWouldSubstitute( *Font,  uint16,  []GID,  bool) bool {
	 := .face.GSUB
	if int() >= len(.Lookups) {
		return false
	}
	 := wouldApplyContext{, nil, }

	 := lookupGSUB(.Lookups[])
	return .wouldApply(&, &.gsubAccels[])
}

// Called before substitution lookups are performed, to ensure that glyph
// class and other properties are set on the glyphs in the buffer.
func layoutSubstituteStart( *Font,  *Buffer) {
	 := .face.GDEF
	 := .GlyphClassDef != nil
	for  := range .Info {
		if  {
			.Info[].glyphProps = .GlyphProps(gID(.Info[].Glyph))
		}
		.Info[].ligProps = 0
		.Info[].syllable = 0
	}
}

func otLayoutDeleteGlyphsInplace( *Buffer,  func(*GlyphInfo) bool) {
	// Merge clusters and delete filtered glyphs.
	var (
		    int
		 = .Info
		  = .Pos
	)
	for  := range  {
		if (&[]) {
			/* Merge clusters.
			* Same logic as buffer.delete_glyph(), but for in-place removal. */

			 := [].Cluster
			if +1 < len(.Info) &&  == [+1].Cluster {
				/* Cluster survives; do nothing. */
				continue
			}

			if  != 0 {
				/* Merge cluster backward. */
				if  < [-1].Cluster {
					 := [].Mask
					 := [-1].Cluster
					for  := ;  != 0 && [-1].Cluster == ; -- {
						[-1].setCluster(, )
					}
				}
				continue
			}

			if +1 < len(.Info) {
				/* Merge cluster forward. */
				.mergeClusters(, +2)
			}

			continue
		}

		if  !=  {
			[] = []
			[] = []
		}
		++
	}
	.Info = .Info[:]
	.Pos = .Pos[:]
}

// Called before positioning lookups are performed, to ensure that glyph
// attachment types and glyph-attachment chains are set for the glyphs in the buffer.
func otLayoutPositionStart( *Font,  *Buffer) {
	positionStartGPOS()
}

// Called after positioning lookups are performed, to finish glyph offsets.
func otLayoutPositionFinishOffsets( *Font,  *Buffer) {
	positionFinishOffsetsGPOS(, )
}

func glyphInfoSubstituted( *GlyphInfo) bool {
	return (.glyphProps & substituted) != 0
}

func clearSubstitutionFlags( *otShapePlan,  *Font,  *Buffer) bool {
	 := .Info
	for  := range  {
		[].glyphProps &= ^substituted
	}
	return false
}

func reverseGraphemes( *Buffer) {
	// MONOTONE_GRAPHEMES was already applied and is taken care of by _hb_grapheme_group_func.
	// So we just check for MONOTONE_CHARACTERS here.
	.reverseGroups(func(,  *GlyphInfo) bool { return .isContinuation() }, .ClusterLevel == MonotoneGraphemes)
}