package harfbuzz

import (
	ucd 
	
)

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

/* Thai / Lao shaper */

var _ otComplexShaper = complexShaperThai{}

type complexShaperThai struct {
	complexShaperNil
}

/* PUA shaping */

// thai_consonant_type_t
const (
	tcNC = iota
	tcAC
	tcRC
	tcDC
	tcNOTCONSONANT
	numConsonantTypes = tcNOTCONSONANT
)

func getConsonantType( rune) uint8 {
	switch  {
	case 0x0E1B, 0x0E1D, 0x0E1F /* , 0x0E2C*/ :
		return tcAC
	case 0x0E0D, 0x0E10:
		return tcRC
	case 0x0E0E, 0x0E0F:
		return tcDC
	}
	if 0x0E01 <=  &&  <= 0x0E2E {
		return tcNC
	}
	return tcNOTCONSONANT
}

// thai_mark_type_t
const (
	tmAV = iota
	tmBV
	tmT
	tmNOTMARK
	numMarkTypes = tmNOTMARK
)

func getMarkType( rune) uint8 {
	if  == 0x0E31 || (0x0E34 <=  &&  <= 0x0E37) ||
		 == 0x0E47 || (0x0E4D <=  &&  <= 0x0E4E) {
		return tmAV
	}
	if 0x0E38 <=  &&  <= 0x0E3A {
		return tmBV
	}
	if 0x0E48 <=  &&  <= 0x0E4C {
		return tmT
	}
	return tmNOTMARK
}

// thai_action_t
const (
	tcNOP = iota
	tcSD  /* Shift combining-mark down */
	tcSL  /* Shift combining-mark left */
	tcSDL /* Shift combining-mark down-left */
	tcRD  /* Remove descender from base */
)

type thaiPuaMapping struct {
	u, winPua, macPua rune
}

var (
	sdMappings = [...]thaiPuaMapping{
		{0x0E48, 0xF70A, 0xF88B}, /* MAI EK */
		{0x0E49, 0xF70B, 0xF88E}, /* MAI THO */
		{0x0E4A, 0xF70C, 0xF891}, /* MAI TRI */
		{0x0E4B, 0xF70D, 0xF894}, /* MAI CHATTAWA */
		{0x0E4C, 0xF70E, 0xF897}, /* THANTHAKHAT */
		{0x0E38, 0xF718, 0xF89B}, /* SARA U */
		{0x0E39, 0xF719, 0xF89C}, /* SARA UU */
		{0x0E3A, 0xF71A, 0xF89D}, /* PHINTHU */
		{0x0000, 0x0000, 0x0000},
	}
	sdlMappings = [...]thaiPuaMapping{
		{0x0E48, 0xF705, 0xF88C}, /* MAI EK */
		{0x0E49, 0xF706, 0xF88F}, /* MAI THO */
		{0x0E4A, 0xF707, 0xF892}, /* MAI TRI */
		{0x0E4B, 0xF708, 0xF895}, /* MAI CHATTAWA */
		{0x0E4C, 0xF709, 0xF898}, /* THANTHAKHAT */
		{0x0000, 0x0000, 0x0000},
	}
	slMappings = [...]thaiPuaMapping{
		{0x0E48, 0xF713, 0xF88A}, /* MAI EK */
		{0x0E49, 0xF714, 0xF88D}, /* MAI THO */
		{0x0E4A, 0xF715, 0xF890}, /* MAI TRI */
		{0x0E4B, 0xF716, 0xF893}, /* MAI CHATTAWA */
		{0x0E4C, 0xF717, 0xF896}, /* THANTHAKHAT */
		{0x0E31, 0xF710, 0xF884}, /* MAI HAN-AKAT */
		{0x0E34, 0xF701, 0xF885}, /* SARA I */
		{0x0E35, 0xF702, 0xF886}, /* SARA II */
		{0x0E36, 0xF703, 0xF887}, /* SARA UE */
		{0x0E37, 0xF704, 0xF888}, /* SARA UEE */
		{0x0E47, 0xF712, 0xF889}, /* MAITAIKHU */
		{0x0E4D, 0xF711, 0xF899}, /* NIKHAHIT */
		{0x0000, 0x0000, 0x0000},
	}
	rdMappings = [...]thaiPuaMapping{
		{0x0E0D, 0xF70F, 0xF89A}, /* YO YING */
		{0x0E10, 0xF700, 0xF89E}, /* THO THAN */
		{0x0000, 0x0000, 0x0000},
	}
)

func thaiPuaShape( rune,  uint8,  *Font) rune {
	var  []thaiPuaMapping
	switch  {
	case tcNOP:
		return 
	case tcSD:
		 = sdMappings[:]
	case tcSDL:
		 = sdlMappings[:]
	case tcSL:
		 = slMappings[:]
	case tcRD:
		 = rdMappings[:]
	}
	for ,  := range  {
		if .u ==  {
			,  := .face.NominalGlyph(.winPua)
			if  {
				return .winPua
			}
			_,  = .face.NominalGlyph(.macPua)
			if  {
				return .macPua
			}
			break
		}
	}
	return 
}

const (
	/* Cluster above looks like: */
	tcT0 = iota /*  ⣤                      */
	tcT1        /*     ⣼                   */
	tcT2        /*        ⣾                */
	tcT3        /*           ⣿             */
	numAboveStates
)

var thaiAboveStartState = [numConsonantTypes + 1] /* For NOT_CONSONANT */ uint8{
	tcT0, /* NC */
	tcT1, /* AC */
	tcT0, /* RC */
	tcT0, /* DC */
	tcT3, /* NOT_CONSONANT */
}

var thaiAboveStateMachine = [numAboveStates][numMarkTypes]struct {
	action    uint8
	nextState uint8
}{ /*AV*/ /*BV*/ /*T*/
	/*T0*/ {{tcNOP, tcT3}, {tcNOP, tcT0}, {tcSD, tcT3}},
	/*T1*/ {{tcSL, tcT2}, {tcNOP, tcT1}, {tcSDL, tcT2}},
	/*T2*/ {{tcNOP, tcT3}, {tcNOP, tcT2}, {tcSL, tcT3}},
	/*T3*/ {{tcNOP, tcT3}, {tcNOP, tcT3}, {tcNOP, tcT3}},
}

// thai_below_state_t
const (
	tbB0 = iota /* No descender */
	tbB1        /* Removable descender */
	tbB2        /* Strict descender */
	numBelowStates
)

var thaiBelowStartState = [numConsonantTypes + 1] /* For NOT_CONSONANT */ uint8{
	tbB0, /* NC */
	tbB0, /* AC */
	tbB1, /* RC */
	tbB2, /* DC */
	tbB2, /* NOT_CONSONANT */
}

var thaiBelowStateMachine = [numBelowStates][numMarkTypes]struct {
	action    uint8
	nextState uint8
}{ /*AV*/ /*BV*/ /*T*/
	/*B0*/ {{tcNOP, tbB0}, {tcNOP, tbB2}, {tcNOP, tbB0}},
	/*B1*/ {{tcNOP, tbB1}, {tcRD, tbB2}, {tcNOP, tbB1}},
	/*B2*/ {{tcNOP, tbB2}, {tcSD, tbB2}, {tcNOP, tbB2}},
}

func doThaiPuaShaping( *Buffer,  *Font) {
	 := thaiAboveStartState[tcNOTCONSONANT]
	 := thaiBelowStartState[tcNOTCONSONANT]
	 := 0

	 := .Info
	//    unsigned int count = buffer.len;
	for  := range  {
		 := getMarkType([].codepoint)

		if  == tmNOTMARK {
			 := getConsonantType([].codepoint)
			 = thaiAboveStartState[]
			 = thaiBelowStartState[]
			 = 
			continue
		}

		 := &thaiAboveStateMachine[][]
		 := &thaiBelowStateMachine[][]
		 = .nextState
		 = .nextState

		// at least one of the above/below actions is NOP.
		 := .action
		if .action != tcNOP {
			 = .action
		}

		.unsafeToBreak(, )
		if  == tcRD {
			[].codepoint = thaiPuaShape([].codepoint, , )
		} else {
			[].codepoint = thaiPuaShape([].codepoint, , )
		}
	}
}

/* We only get one script at a time, so a script-agnostic implementation
* is adequate here. */
func isSaraAm( rune) bool           { return  & ^0x0080 == 0x0E33 }
func nikhahitFromSaraAm( rune) rune { return  - 0x0E33 + 0x0E4D }
func saraAaFromSaraAm( rune) rune   { return  - 1 }
func isAboveBaseMark( rune) bool {
	 :=  & ^0x0080
	return 0x0E34 <=  &&  <= 0x0E37 ||
		0x0E47 <=  &&  <= 0x0E4E ||
		 == 0x0E31 ||
		 == 0x0E3B
}

/* This function implements the shaping logic documented here:
 *
 *   https://linux.thai.net/~thep/th-otf/shaping.html
 *
 * The first shaping rule listed there is needed even if the font has Thai
 * OpenType tables.  The rest do fallback positioning based on PUA codepoints.
 * We implement that only if there exist no Thai GSUB in the font.
 */
func (complexShaperThai) ( *otShapePlan,  *Buffer,  *Font) {
	// The following is NOT specified in the MS OT Thai spec, however, it seems
	// to be what Uniscribe and other engines implement.  According to Eric Muller:
	//
	// When you have a SARA AM, decompose it in NIKHAHIT + SARA AA, *and* move the
	// NIKHAHIT backwards over any above-base marks.
	//
	// <0E14, 0E4B, 0E33> . <0E14, 0E4D, 0E4B, 0E32>
	//
	// This reordering is legit only when the NIKHAHIT comes from a SARA AM, not
	// when it's there to start with. The string <0E14, 0E4B, 0E4D> is probably
	// not what a user wanted, but the rendering is nevertheless nikhahit above
	// chattawa.
	//
	// Same for Lao.
	//
	// Note:
	//
	// Uniscribe also does some below-marks reordering.  Namely, it positions U+0E3A
	// after U+0E38 and U+0E39.  We do that by modifying the ccc for U+0E3A.
	// See unicode.modified_combining_class ().  Lao does NOT have a U+0E3A
	// equivalent.
	//

	//
	// Here are the characters of significance:
	//
	//			Thai	Lao
	// SARA AM:		U+0E33	U+0EB3
	// SARA AA:		U+0E32	U+0EB2
	// Nikhahit:		U+0E4D	U+0ECD
	//
	// Testing shows that Uniscribe reorder the following marks:
	// Thai:	<0E31,0E34..0E37,     0E47..0E4E>
	// Lao:	<0EB1,0EB4..0EB7,0EBB,0EC8..0ECD>
	//
	// Note how the Lao versions are the same as Thai + 0x80.
	//

	.clearOutput()
	 := len(.Info)
	for .idx = 0; .idx < ; {
		 := .cur(0).codepoint
		if !isSaraAm() {
			.nextGlyph()
			continue
		}

		/* Is SARA AM. Decompose and reorder. */
		.outputRune(nikhahitFromSaraAm())
		.prev().setContinuation()
		.replaceGlyph(saraAaFromSaraAm())

		/* Make Nikhahit be recognized as a ccc=0 mark when zeroing widths. */
		 := len(.outInfo)
		.outInfo[-2].setGeneralCategory(ucd.Mn)

		/* Ok, let's see... */
		 :=  - 2
		for  > 0 && isAboveBaseMark(.outInfo[-1].codepoint) {
			--
		}

		if +2 <  {
			/* Move Nikhahit (end-2) to the beginning */
			.mergeOutClusters(, )
			 := .outInfo[-2]
			copy(.outInfo[+1:], .outInfo[:-2])
			.outInfo[] = 
		} else {
			/* Since we decomposed, and NIKHAHIT is combining, merge clusters with the
			* previous cluster. */
			if  != 0 {
				.mergeOutClusters(-1, )
			}
		}
	}
	.swapBuffers()

	/* If font has Thai GSUB, we are done. */
	if .props.Script == language.Thai && !.otMap.foundScript[0] {
		doThaiPuaShaping(, )
	}
}

func (complexShaperThai) () (zeroWidthMarks, bool) {
	return zeroWidthMarksByGdefLate, false
}

func (complexShaperThai) () normalizationMode {
	return nmDefault
}