package harfbuzz

import (
	

	ot 
	
	ucd 
)

// ported from harfbuzz/src/hb-ot-shape-complex-khmer.cc Copyright © 2011,2012  Google, Inc. Behdad Esfahbod

var _ otComplexShaper = (*complexShaperKhmer)(nil)

// Khmer shaper
type complexShaperKhmer struct {
	plan khmerShapePlan
}

var khmerFeatures = [...]otMapFeature{
	/*
	* Basic features.
	* These features are applied in order, one at a time, after reordering.
	 */
	{ot.NewTag('p', 'r', 'e', 'f'), ffManualJoiners | ffPerSyllable},
	{ot.NewTag('b', 'l', 'w', 'f'), ffManualJoiners | ffPerSyllable},
	{ot.NewTag('a', 'b', 'v', 'f'), ffManualJoiners | ffPerSyllable},
	{ot.NewTag('p', 's', 't', 'f'), ffManualJoiners | ffPerSyllable},
	{ot.NewTag('c', 'f', 'a', 'r'), ffManualJoiners | ffPerSyllable},
	/*
	* Other features.
	* These features are applied all at once after clearing syllables.
	 */
	{ot.NewTag('p', 'r', 'e', 's'), ffGlobalManualJoiners},
	{ot.NewTag('a', 'b', 'v', 's'), ffGlobalManualJoiners},
	{ot.NewTag('b', 'l', 'w', 's'), ffGlobalManualJoiners},
	{ot.NewTag('p', 's', 't', 's'), ffGlobalManualJoiners},
}

// Must be in the same order as the khmerFeatures array.
const (
	khmerPref = iota
	khmerBlwf
	khmerAbvf
	khmerPstf
	khmerCfar

	khmerPres
	khmerAbvs
	khmerBlws
	khmerPsts

	khmerNumFeatures
	khmerBasicFeatures = khmerPres /* Don't forget to update this! */
)

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

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

	/* Testing suggests that Uniscribe does NOT pause between basic
	* features.  Test with KhmerUI.ttf and the following three
	* sequences:
	*
	*   U+1789,U+17BC
	*   U+1789,U+17D2,U+1789
	*   U+1789,U+17D2,U+1789,U+17BC
	*
	* https://github.com/harfbuzz/harfbuzz/issues/974
	 */
	.enableFeatureExt(ot.NewTag('l', 'o', 'c', 'l'), ffPerSyllable, 1)
	.enableFeatureExt(ot.NewTag('c', 'c', 'm', 'p'), ffPerSyllable, 1)

	 := 0
	for ;  < khmerBasicFeatures; ++ {
		.addFeatureExt(khmerFeatures[].tag, khmerFeatures[].flags, 1)
	}

	// https://github.com/harfbuzz/harfbuzz/issues/3531
	.addGSUBPause(nil)

	for ;  < khmerNumFeatures; ++ {
		.addFeatureExt(khmerFeatures[].tag, khmerFeatures[].flags, 1)
	}
}

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

	/* Khmer spec has 'clig' as part of required shaping features:
	* "Apply feature 'clig' to form ligatures that are desired for
	* typographical correctness.", hence in overrides... */
	.enableFeature(ot.NewTag('c', 'l', 'i', 'g'))

	.disableFeature(ot.NewTag('l', 'i', 'g', 'a'))
}

type khmerShapePlan struct {
	viramaGlyph GID
	maskArray   [khmerNumFeatures]GlyphMask
}

func ( *complexShaperKhmer) ( *otShapePlan) {
	var  khmerShapePlan

	.viramaGlyph = ^GID(0)

	for  := range .maskArray {
		if khmerFeatures[].flags&ffGLOBAL == 0 {
			.maskArray[] = .otMap.getMask1(khmerFeatures[].tag)
		}
	}

	.plan = 
}

func ( *complexShaperKhmer) ( *otShapePlan,  *Buffer,  *Font) {
	/* We cannot setup masks here.  We save information about characters
	* and setup masks later on in a pause-callback. */

	 := .Info
	for  := range  {
		setKhmerProperties(&[])
	}
}

func setKhmerProperties( *GlyphInfo) {
	 := .codepoint
	 := indicGetCategories()
	.complexCategory = uint8( & 0xFF)
}

func setupSyllablesKhmer( *otShapePlan,  *Font,  *Buffer) bool {
	findSyllablesKhmer()
	,  := .syllableIterator()
	for ,  := .next();  < ; ,  = .next() {
		.unsafeToBreak(, )
	}
	return false
}

func foundSyllableKhmer( uint8, ,  int,  []GlyphInfo,  *uint8) {
	for  := ;  < ; ++ {
		[].syllable = (* << 4) | 
	}
	*++
	if * == 16 {
		* = 1
	}
}

/* Rules from:
 * https://docs.microsoft.com/en-us/typography/script-development/devanagari */
func ( *khmerShapePlan) ( *Buffer, ,  int) {
	 := .Info

	/* Setup masks. */
	{
		/* Post-base */
		 := .maskArray[khmerBlwf] |
			.maskArray[khmerAbvf] |
			.maskArray[khmerPstf]
		for  :=  + 1;  < ; ++ {
			[].Mask |= 
		}
	}

	 := 0
	for  :=  + 1;  < ; ++ {
		/* """
		 * When a COENG + (Cons | IndV) combination are found (and subscript count
		 * is less than two) the character combination is handled according to the
		 * subscript type of the character following the COENG.
		 *
		 * ...
		 *
		 * Subscript Type 2 - The COENG + RO characters are reordered to immediately
		 * before the base glyph. Then the COENG + RO characters are assigned to have
		 * the 'pref' OpenType feature applied to them.
		 * """
		 */
		if [].complexCategory == khmSM_ex_H &&  <= 2 && +1 <  {
			++

			if [+1].complexCategory == khmSM_ex_Ra {
				for  := 0;  < 2; ++ {
					[+].Mask |= .maskArray[khmerPref]
				}

				/* Move the Coeng,Ro sequence to the start. */
				.mergeClusters(, +2)
				 := []
				 := [+1]
				copy([+2:], [:])
				[] = 
				[+1] = 

				/* Mark the subsequent stuff with 'cfar'.  Used in Khmer.
				 * Read the feature spec.
				 * This allows distinguishing the following cases with MS Khmer fonts:
				 * U+1784,U+17D2,U+179A,U+17D2,U+1782
				 * U+1784,U+17D2,U+1782,U+17D2,U+179A
				 */
				if .maskArray[khmerCfar] != 0 {
					for  :=  + 2;  < ; ++ {
						[].Mask |= .maskArray[khmerCfar]
					}
				}

				 = 2 /* Done. */
			}
		} else if [].complexCategory == khmSM_ex_VPre { /* Reorder left matra piece. */
			/* Move to the start. */
			.mergeClusters(, +1)
			 := []
			copy([+1:], [:])
			[] = 
		}
	}
}

func ( *complexShaperKhmer) ( *Buffer, ,  int) {
	 := .Info[].syllable & 0x0F
	switch  {
	case khmerBrokenCluster, /* We already inserted dotted-circles, so just call the consonant_syllable. */
		khmerConsonantSyllable:
		.plan.reorderConsonantSyllable(, , )
	}
}

func ( *complexShaperKhmer) ( *otShapePlan,  *Font,  *Buffer) bool {
	if debugMode {
		fmt.Println("KHMER - start reordering khmer")
	}

	 := syllabicInsertDottedCircles(, , khmerBrokenCluster, khmSM_ex_DOTTEDCIRCLE, -1, -1)
	,  := .syllableIterator()
	for ,  := .next();  < ; ,  = .next() {
		.reorderSyllableKhmer(, , )
	}

	if debugMode {
		fmt.Println("KHMER - end reordering khmer")
	}

	return 
}

func (complexShaperKhmer) ( *otNormalizeContext,  rune) (rune, rune, bool) {
	switch  {
	/*
	 * Decompose split matras that don't have Unicode decompositions.
	 */

	/* Khmer */
	case 0x17BE:
		return 0x17C1, 0x17BE, true
	case 0x17BF:
		return 0x17C1, 0x17BF, true
	case 0x17C0:
		return 0x17C1, 0x17C0, true
	case 0x17C4:
		return 0x17C1, 0x17C4, true
	case 0x17C5:
		return 0x17C1, 0x17C5, true
	}

	return ucd.Decompose()
}

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

	return ucd.Compose(, )
}

func (complexShaperKhmer) () (zeroWidthMarks, bool) {
	return zeroWidthMarksNone, false
}

func (complexShaperKhmer) () normalizationMode {
	return nmComposedDiacriticsNoShortCircuit
}

func (complexShaperKhmer) () tables.Tag                         { return 0 }
func (complexShaperKhmer) (*otShapePlan, *Buffer, *Font) {}
func (complexShaperKhmer) (*otShapePlan, *Buffer, *Font) {
}
func (complexShaperKhmer) (*otShapePlan, *Buffer, int, int) {}