package harfbuzz

import (
	

	ot 
	
	ucd 
)

// ported from harfbuzz/src/hb-ot-shape-complex-use.cc Copyright © 2015  Mozilla Foundation. Google, Inc. Jonathan Kew, Behdad Esfahbod

/*
 * Universal Shaping Engine.
 * https://docs.microsoft.com/en-us/typography/script-development/use
 */

var _ otComplexShaper = (*complexShaperUSE)(nil)

/*
 * Basic features.
 * These features are applied all at once, before reordering.
 */
var useBasicFeatures = [...]tables.Tag{
	ot.NewTag('r', 'k', 'r', 'f'),
	ot.NewTag('a', 'b', 'v', 'f'),
	ot.NewTag('b', 'l', 'w', 'f'),
	ot.NewTag('h', 'a', 'l', 'f'),
	ot.NewTag('p', 's', 't', 'f'),
	ot.NewTag('v', 'a', 't', 'u'),
	ot.NewTag('c', 'j', 'c', 't'),
}

var useTopographicalFeatures = [...]tables.Tag{
	ot.NewTag('i', 's', 'o', 'l'),
	ot.NewTag('i', 'n', 'i', 't'),
	ot.NewTag('m', 'e', 'd', 'i'),
	ot.NewTag('f', 'i', 'n', 'a'),
}

/* Same order as useTopographicalFeatures. */
const (
	joiningFormIsol = iota
	joiningFormInit
	joiningFormMedi
	joiningFormFina
	joiningFormNone
)

/*
 * Other features.
 * These features are applied all at once, after reordering and
 * clearing syllables.
 */
var useOtherFeatures = [...]tables.Tag{
	ot.NewTag('a', 'b', 'v', 's'),
	ot.NewTag('b', 'l', 'w', 's'),
	ot.NewTag('h', 'a', 'l', 'n'),
	ot.NewTag('p', 'r', 'e', 's'),
	ot.NewTag('p', 's', 't', 's'),
}

type useShapePlan struct {
	arabicPlan *arabicShapePlan
	rphfMask   GlyphMask
}

type complexShaperUSE struct {
	complexShaperNil

	plan useShapePlan
}

func ( *complexShaperUSE) ( *otShapePlanner) {
	 := &.otMap

	/* Do this before any lookups have been applied. */
	.addGSUBPause(.setupSyllablesUse)

	/* "Default glyph pre-processing group" */
	.enableFeatureExt(ot.NewTag('l', 'o', 'c', 'l'), ffPerSyllable, 1)
	.enableFeatureExt(ot.NewTag('c', 'c', 'm', 'p'), ffPerSyllable, 1)
	.enableFeatureExt(ot.NewTag('n', 'u', 'k', 't'), ffPerSyllable, 1)
	.enableFeatureExt(ot.NewTag('a', 'k', 'h', 'n'), ffManualZWJ|ffPerSyllable, 1)

	/* "Reordering group" */
	.addGSUBPause(clearSubstitutionFlags)
	.addFeatureExt(ot.NewTag('r', 'p', 'h', 'f'), ffManualZWJ|ffPerSyllable, 1)
	.addGSUBPause(.recordRphfUse)
	.addGSUBPause(clearSubstitutionFlags)
	.enableFeatureExt(ot.NewTag('p', 'r', 'e', 'f'), ffManualZWJ|ffPerSyllable, 1)
	.addGSUBPause(recordPrefUse)

	/* "Orthographic unit shaping group" */
	for ,  := range useBasicFeatures {
		.enableFeatureExt(, ffManualZWJ|ffPerSyllable, 1)
	}

	.addGSUBPause(reorderUse)
	.addGSUBPause(nil)

	/* "Topographical features" */
	for ,  := range useTopographicalFeatures {
		.addFeature()
	}
	.addGSUBPause(nil)

	/* "Standard typographic presentation" */
	for ,  := range useOtherFeatures {
		.enableFeatureExt(, ffManualZWJ, 1)
	}
}

func ( *complexShaperUSE) ( *otShapePlan) {
	var  useShapePlan

	.rphfMask = .otMap.getMask1(ot.NewTag('r', 'p', 'h', 'f'))

	if hasArabicJoining(.props.Script) {
		 := newArabicPlan()
		.arabicPlan = &
	}

	.plan = 
}

func ( *complexShaperUSE) ( *otShapePlan,  *Buffer,  *Font) {
	 := .plan
	/* Do this before allocating complexCategory. */
	if .arabicPlan != nil {
		.arabicPlan.setupMasks(, .props.Script)
	}

	/* We cannot setup masks here.  We save information about characters
	* and setup masks later on in a pause-callback. */

	 := .Info
	for  := range  {
		[].complexCategory = useLookup([].codepoint)
	}
}

func ( *complexShaperUSE) ( *Buffer) {
	 := .plan

	 := .rphfMask
	if  == 0 {
		return
	}

	 := .Info
	,  := .syllableIterator()
	for ,  := .next();  < ; ,  = .next() {
		 := 1
		if [].complexCategory != useSM_ex_R {
			 = min(3, -)
		}
		for  := ;  < +; ++ {
			[].Mask |= 
		}
	}
}

func ( *complexShaperUSE) ( *otShapePlan,  *Buffer) {
	if .plan.arabicPlan != nil {
		return
	}
	var (
		    [4]GlyphMask
		 uint32
	)
	for  := range  {
		[] = .otMap.getMask1(useTopographicalFeatures[])
		if [] == .otMap.globalMask {
			[] = 0
		}
		 |= []
	}
	if  == 0 {
		return
	}
	 := ^

	 := 0
	 := joiningFormNone
	 := .Info
	,  := .syllableIterator()
	for ,  := .next();  < ; ,  = .next() {
		 := [].syllable & 0x0F
		switch  {
		case useHieroglyphCluster, useNonCluster:
			// these don't join.  Nothing to do.
			 = joiningFormNone

		case useViramaTerminatedCluster, useSakotTerminatedCluster, useStandardCluster, useNumberJoinerTerminatedCluster,
			useNumeralCluster, useSymbolCluster, useBrokenCluster:
			 :=  == joiningFormFina ||  == joiningFormIsol
			if  {
				// fixup previous syllable's form.
				if  == joiningFormFina {
					 = joiningFormMedi
				} else {
					 = joiningFormInit
				}
				for  := ;  < ; ++ {
					[].Mask = ([].Mask & ) | []
				}
			}

			// form for this syllable.
			 = joiningFormIsol
			if  {
				 = joiningFormFina
			}
			for  := ;  < ; ++ {
				[].Mask = ([].Mask & ) | []
			}
		}

		 = 
	}
}

func ( *complexShaperUSE) ( *otShapePlan,  *Font,  *Buffer) bool {
	findSyllablesUse()
	,  := .syllableIterator()
	for ,  := .next();  < ; ,  = .next() {
		.unsafeToBreak(, )
	}
	.setupRphfMask()
	.setupTopographicalMasks(, )
	return false
}

func ( *complexShaperUSE) ( *otShapePlan,  *Font,  *Buffer) bool {
	 := .plan

	 := .rphfMask
	if  == 0 {
		return false
	}
	 := .Info

	,  := .syllableIterator()
	for ,  := .next();  < ; ,  = .next() {
		// mark a substituted repha as USE(R).
		for  := ;  <  && ([].Mask&) != 0; ++ {
			if glyphInfoSubstituted(&[]) {
				[].complexCategory = useSM_ex_R
				break
			}
		}
	}
	return false
}

func recordPrefUse( *otShapePlan,  *Font,  *Buffer) bool {
	 := .Info

	,  := .syllableIterator()
	for ,  := .next();  < ; ,  = .next() {
		// mark a substituted pref as VPre, as they behave the same way.
		for  := ;  < ; ++ {
			if glyphInfoSubstituted(&[]) {
				[].complexCategory = useSM_ex_VPre
				break
			}
		}
	}
	return false
}

func isHalantUse( *GlyphInfo) bool {
	return (.complexCategory == useSM_ex_H || .complexCategory == useSM_ex_HVM || .complexCategory == useSM_ex_IS) &&
		!.ligated()
}

func reorderSyllableUse( *Buffer, ,  int) {
	 := (.Info[].syllable & 0x0F)
	/* Only a few syllable types need reordering. */
	const  = 1<<useViramaTerminatedCluster |
		1<<useSakotTerminatedCluster |
		1<<useStandardCluster |
		1<<useSymbolCluster |
		1<<useBrokenCluster
	if 1<<& == 0 {
		return
	}

	 := .Info

	const  int64 = (1<<useSM_ex_FAbv |
		1<<useSM_ex_FBlw |
		1<<useSM_ex_FPst |
		1<<useSM_ex_FMAbv |
		1<<useSM_ex_FMBlw |
		1<<useSM_ex_FMPst |
		1<<useSM_ex_MAbv |
		1<<useSM_ex_MBlw |
		1<<useSM_ex_MPst |
		1<<useSM_ex_MPre |
		1<<useSM_ex_VAbv |
		1<<useSM_ex_VBlw |
		1<<useSM_ex_VPst |
		1<<useSM_ex_VPre |
		1<<useSM_ex_VMAbv |
		1<<useSM_ex_VMBlw |
		1<<useSM_ex_VMPst |
		1<<useSM_ex_VMPre)

	/* Move things forward. */
	if [].complexCategory == useSM_ex_R && - > 1 {
		/* Got a repha.  Reorder it towards the end, but before the first post-base
		 * glyph. */
		for  :=  + 1;  < ; ++ {
			 := (int64(1<<([].complexCategory))&) != 0 ||
				isHalantUse(&[])
			if  ||  == -1 {
				/* If we hit a post-base glyph, move before it; otherwise move to the
				 * end. Shift things in between backward. */

				if  {
					--
				}

				.mergeClusters(, +1)
				 := []
				copy([:], [+1:])
				[] = 

				break
			}
		}
	}

	/* Move things back. */
	 := 
	for  := ;  < ; ++ {
		 := 1 << ([].complexCategory)
		if isHalantUse(&[]) {
			/* If we hit a halant, move after it; otherwise move to the beginning, and
			* shift things in between forward. */
			 =  + 1
		} else if &(1<<useSM_ex_VPre|1<<useSM_ex_VMPre) != 0 &&
			/* Only move the first component of a MultipleSubst. */
			[].getLigComp() == 0 &&  <  {
			.mergeClusters(, +1)
			 := []
			copy([+1:], [:])
			[] = 
		}
	}
}

func reorderUse( *otShapePlan,  *Font,  *Buffer) bool {
	if debugMode {
		fmt.Println("USE - start reordering USE")
	}
	 := syllabicInsertDottedCircles(, , useBrokenCluster,
		useSM_ex_B, useSM_ex_R, -1)

	,  := .syllableIterator()
	for ,  := .next();  < ; ,  = .next() {
		reorderSyllableUse(, , )
	}
	if debugMode {
		fmt.Println("USE - end reordering USE:", .Info)
	}

	return 
}

func ( *complexShaperUSE) ( *otShapePlan,  *Buffer,  *Font) {
	preprocessTextVowelConstraints()
}

func ( *complexShaperUSE) ( *otNormalizeContext, ,  rune) (rune, bool) {
	// avoid recomposing split matras.
	if ucd.LookupGeneralCategory().IsMark() {
		return 0, false
	}

	return ucd.Compose(, )
}

func (complexShaperUSE) () (zeroWidthMarks, bool) {
	return zeroWidthMarksByGdefEarly, false
}

func (complexShaperUSE) () normalizationMode {
	return nmComposedDiacriticsNoShortCircuit
}