package harfbuzz

import (
	
	

	
	
)

const maxContextLength = 64

var _ layoutLookup = lookupGSUB{}

// implements layoutLookup
type lookupGSUB font.GSUBLookup

func ( lookupGSUB) () uint32 { return .LookupOptions.Props() }

func ( lookupGSUB) ( *setDigest) {
	for ,  := range .Subtables {
		.collectCoverage(.Cov())
	}
}

func ( lookupGSUB) ( *getSubtablesContext) {
	for ,  := range .Subtables {
		* = append(*, newGSUBApplicable())
	}
}

func ( lookupGSUB) ( *otApplyContext) bool {
	for ,  := range .Subtables {
		if .applyGSUB() {
			return true
		}
	}
	return false
}

func ( lookupGSUB) ( *wouldApplyContext,  *otLayoutLookupAccelerator) bool {
	if len(.glyphs) == 0 {
		return false
	}
	if !.digest.mayHave(gID(.glyphs[0])) {
		return false
	}
	// dispatch on subtables
	for ,  := range .Subtables {
		if .wouldApplyGSUB() {
			return true
		}
	}
	return false
}

func ( lookupGSUB) () bool {
	if len(.Subtables) == 0 {
		return false
	}
	,  := .Subtables[0].(tables.ReverseChainSingleSubs)
	return 
}

func applyRecurseGSUB( *otApplyContext,  uint16) bool {
	 := .font.face.GSUB
	 := lookupGSUB(.Lookups[])
	return .applyRecurseLookup(, )
}

// matchesLigature tests if the ligature should be applied on `glyphsFromSecond`,
// which starts from the second glyph.
func matchesLigature( tables.Ligature,  []GID) bool {
	if len() != len(.ComponentGlyphIDs) {
		return false
	}
	for ,  := range  {
		if  != GID(.ComponentGlyphIDs[]) {
			return false
		}
	}
	return true
}

// return `true` is we should apply this lookup to the glyphs in `c`,
// which are assumed to be non empty
func ( *wouldApplyContext) ( tables.GSUBLookup) bool {
	,  := .Cov().Index(gID(.glyphs[0]))
	switch data := .(type) {
	case tables.SingleSubs, tables.MultipleSubs, tables.AlternateSubs, tables.ReverseChainSingleSubs:
		return len(.glyphs) == 1 && 

	case tables.LigatureSubs:
		if ! {
			return false
		}
		 := .LigatureSets[].Ligatures
		 := .glyphs[1:]
		for ,  := range  {
			if matchesLigature(, ) {
				return true
			}
		}
		return false

	case tables.ContextualSubs:
		switch inner := .Data.(type) {
		case tables.ContextualSubs1:
			return .wouldApplyLookupContext1(tables.SequenceContextFormat1(), )
		case tables.ContextualSubs2:
			return .wouldApplyLookupContext2(tables.SequenceContextFormat2(), , .glyphs[0])
		case tables.ContextualSubs3:
			return .wouldApplyLookupContext3(tables.SequenceContextFormat3(), )
		}

	case tables.ChainedContextualSubs:
		switch inner := .Data.(type) {
		case tables.ChainedContextualSubs1:
			return .wouldApplyLookupChainedContext1(tables.ChainedSequenceContextFormat1(), )
		case tables.ChainedContextualSubs2:
			return .wouldApplyLookupChainedContext2(tables.ChainedSequenceContextFormat2(), , .glyphs[0])
		case tables.ChainedContextualSubs3:
			return .wouldApplyLookupChainedContext3(tables.ChainedSequenceContextFormat3(), )
		}

	}
	return false
}

// return `true` is the subsitution found a match and was applied
func ( *otApplyContext) ( tables.GSUBLookup) bool {
	 := .buffer.cur(0)
	 := .Glyph
	,  := .Cov().Index(gID())
	if ! {
		return false
	}

	if debugMode {
		fmt.Printf("\t\tAPPLY - type %T at index %d\n", , .buffer.idx)
	}

	switch data := .(type) {
	case tables.SingleSubs:
		switch inner := .Data.(type) {
		case tables.SingleSubstData1:
			/* According to the Adobe Annotated OpenType Suite, result is always
			* limited to 16bit. */
			 = GID(uint16(int() + int(.DeltaGlyphID)))
			.replaceGlyph()
		case tables.SingleSubstData2:
			if  >= len(.SubstituteGlyphIDs) { // index is not sanitized in tables.Parse
				return false
			}
			.replaceGlyph(GID(.SubstituteGlyphIDs[]))
		}

	case tables.MultipleSubs:
		.applySubsSequence(.Sequences[].SubstituteGlyphIDs)

	case tables.AlternateSubs:
		 := .AlternateSets[].AlternateGlyphIDs
		return .applySubsAlternate()

	case tables.LigatureSubs:
		 := .LigatureSets[].Ligatures
		return .applySubsLigature()

	case tables.ContextualSubs:
		switch inner := .Data.(type) {
		case tables.ContextualSubs1:
			return .applyLookupContext1(tables.SequenceContextFormat1(), )
		case tables.ContextualSubs2:
			return .applyLookupContext2(tables.SequenceContextFormat2(), , )
		case tables.ContextualSubs3:
			return .applyLookupContext3(tables.SequenceContextFormat3(), )
		}

	case tables.ChainedContextualSubs:
		switch inner := .Data.(type) {
		case tables.ChainedContextualSubs1:
			return .applyLookupChainedContext1(tables.ChainedSequenceContextFormat1(), )
		case tables.ChainedContextualSubs2:
			return .applyLookupChainedContext2(tables.ChainedSequenceContextFormat2(), , )
		case tables.ChainedContextualSubs3:
			return .applyLookupChainedContext3(tables.ChainedSequenceContextFormat3(), )
		}

	case tables.ReverseChainSingleSubs:
		if .nestingLevelLeft != maxNestingLevel {
			return false // no chaining to this type
		}
		,  := len(.BacktrackCoverages), len(.LookaheadCoverages)

		,  := .matchBacktrack(get1N(&.indices, 0, ), matchCoverage(.BacktrackCoverages))
		if ! {
			return false
		}

		,  := .matchLookahead(get1N(&.indices, 0, ), matchCoverage(.LookaheadCoverages), .buffer.idx+1)
		if ! {
			return false
		}

		.buffer.unsafeToBreakFromOutbuffer(, )
		.setGlyphClass(GID(.SubstituteGlyphIDs[]))
		.buffer.cur(0).Glyph = GID(.SubstituteGlyphIDs[])
		// Note: We DON'T decrease buffer.idx.  The main loop does it
		// for us.  This is useful for preventing surprises if someone
		// calls us through a Context lookup.
	}

	return true
}

func ( *otApplyContext) ( []gID) {
	/* Special-case to make it in-place and not consider this
	 * as a "multiplied" substitution. */
	switch len() {
	case 1:
		.replaceGlyph(GID([0]))
	case 0:
		/* Spec disallows this, but Uniscribe allows it.
		 * https://github.com/harfbuzz/harfbuzz/issues/253 */
		.buffer.deleteGlyph()
	default:
		var  uint16
		if .buffer.cur(0).isLigature() {
			 = tables.GPBaseGlyph
		}
		 := .buffer.cur(0).getLigID()
		for ,  := range  {
			// If is attached to a ligature, don't disturb that.
			// https://github.com/harfbuzz/harfbuzz/issues/3069
			if  == 0 {
				.buffer.cur(0).setLigPropsForMark(0, uint8())
			}
			.setGlyphClassExt(GID(), , false, true)
			.buffer.outputGlyphIndex(GID())
		}
		.buffer.skipGlyph()
	}
}

func ( *otApplyContext) ( []gID) bool {
	 := uint32(len())
	if  == 0 {
		return false
	}

	 := .buffer.cur(0).Mask
	 := .lookupMask

	/* Note: This breaks badly if two features enabled this lookup together. */

	 := bits.TrailingZeros32()
	 := ( & ) >> 

	/* If altIndex is MAX_VALUE, randomize feature if it is the rand feature. */
	if  == otMapMaxValue && .random {
		// Maybe we can do better than unsafe-to-break all; but since we are
		// changing random state, it would be hard to track that.  Good 'nough.
		.buffer.unsafeToBreak(0, len(.buffer.Info))
		 = .randomNumber()% + 1
	}

	if  >  ||  == 0 {
		return false
	}

	.replaceGlyph(GID([-1]))
	return true
}

func ( *otApplyContext) ( tables.Ligature) bool {
	 := len(.ComponentGlyphIDs) + 1

	// Special-case to make it in-place and not consider this
	// as a "ligated" substitution.
	if  == 1 {
		.replaceGlyph(GID(.LigatureGlyph))
		return true
	}

	if  > maxContextLength {
		return false
	}

	, ,  := .matchInput(.ComponentGlyphIDs, matchGlyph)
	if ! {
		.buffer.unsafeToConcat(.buffer.idx, )
		return false
	}

	.ligateInput(, , .LigatureGlyph, )

	return true
}

func ( *otApplyContext) ( []tables.Ligature) bool {
	for ,  := range  {
		if .applyOneLigature() {
			return true
		}
	}
	return false
}