package harfbuzz

import (
	ot 
	
	ucd 
)

// ported from harfbuzz/src/hb-ot-shape-complex-hangul.cc Copyright © 2013  Google, Inc. Behdad Esfahbod

var _ otComplexShaper = (*complexShaperHangul)(nil)

// Hangul shaper
type complexShaperHangul struct {
	complexShaperNil

	plan hangulShapePlan
}

/* Same order as the feature array below */
const (
	_ = iota // jmo

	ljmo
	vjmo
	tjmo

	firstHangulFeature = ljmo
	hangulFeatureCount = tjmo + 1
)

var hangulFeatures = [hangulFeatureCount]tables.Tag{
	0,
	ot.NewTag('l', 'j', 'm', 'o'),
	ot.NewTag('v', 'j', 'm', 'o'),
	ot.NewTag('t', 'j', 'm', 'o'),
}

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

	for  := firstHangulFeature;  < hangulFeatureCount; ++ {
		.addFeature(hangulFeatures[])
	}
}

func (complexShaperHangul) ( *otShapePlanner) {
	/* Uniscribe does not apply 'calt' for Hangul, and certain fonts
	* (Noto Sans CJK, Source Sans Han, etc) apply all of jamo lookups
	* in calt, which is not desirable. */
	.otMap.disableFeature(ot.NewTag('c', 'a', 'l', 't'))
}

type hangulShapePlan struct {
	maskArray [hangulFeatureCount]GlyphMask
}

func ( *complexShaperHangul) ( *otShapePlan) {
	var  hangulShapePlan

	for  := range .maskArray {
		.maskArray[] = .otMap.getMask1(hangulFeatures[])
	}

	.plan = 
}

func isCombiningT( rune) bool {
	return ucd.HangulTBase+1 <=  &&  <= ucd.HangulTBase+ucd.HangulTCount-1
}

func isL( rune) bool {
	return 0x1100 <=  &&  <= 0x115F || 0xA960 <=  &&  <= 0xA97C
}

func isV( rune) bool {
	return 0x1160 <=  &&  <= 0x11A7 || 0xD7B0 <=  &&  <= 0xD7C6
}

func isT( rune) bool {
	return 0x11A8 <=  &&  <= 0x11FF || 0xD7CB <=  &&  <= 0xD7FB
}

func isZeroWidthChar( *Font,  rune) bool {
	,  := .face.NominalGlyph()
	return  && .GlyphHAdvance() == 0
}

func ( *complexShaperHangul) ( *otShapePlan,  *Buffer,  *Font) {
	/* Hangul syllables come in two shapes: LV, and LVT.  Of those:
	*
	*   - LV can be precomposed, or decomposed.  Lets call those
	*     <LV> and <L,V>,
	*   - LVT can be fully precomposed, partially precomposed, or
	*     fully decomposed.  Ie. <LVT>, <LV,T>, or <L,V,T>.
	*
	* The composition / decomposition is mechanical.  However, not
	* all <L,V> sequences compose, and not all <LV,T> sequences
	* compose.
	*
	* Here are the specifics:
	*
	*   - <L>: U+1100..115F, U+A960..A97F
	*   - <V>: U+1160..11A7, U+D7B0..D7C7
	*   - <T>: U+11A8..11FF, U+D7CB..D7FB
	*
	*   - Only the <L,V> sequences for some of the U+11xx ranges combine.
	*   - Only <LV,T> sequences for some of the Ts in U+11xx range combine.
	*
	* Here is what we want to accomplish in this shaper:
	*
	*   - If the whole syllable can be precomposed, do that,
	*   - Otherwise, fully decompose and apply ljmo/vjmo/tjmo features.
	*   - If a valid syllable is followed by a Hangul tone mark, reorder the tone
	*     mark to precede the whole syllable - unless it is a zero-width glyph, in
	*     which case we leave it untouched, assuming it's designed to overstrike.
	*
	* That is, of the different possible syllables:
	*
	*   <L>
	*   <L,V>
	*   <L,V,T>
	*   <LV>
	*   <LVT>
	*   <LV, T>
	*
	* - <L> needs no work.
	*
	* - <LV> and <LVT> can stay the way they are if the font supports them, otherwise we
	*   should fully decompose them if font supports.
	*
	* - <L,V> and <L,V,T> we should compose if the whole thing can be composed.
	*
	* - <LV,T> we should compose if the whole thing can be composed, otherwise we should
	*   decompose.
	 */

	.clearOutput()
	// Extent of most recently seen syllable; valid only if start < end
	var ,  int
	 := len(.Info)

	for .idx = 0; .idx < ; {
		 := .cur(0).codepoint

		if 0x302E <=  &&  <= 0x302F { // isHangulTone
			/*
			* We could cache the width of the tone marks and the existence of dotted-circle,
			* but the use of the Hangul tone mark characters seems to be rare enough that
			* I didn't bother for now.
			 */
			if  <  &&  == len(.outInfo) {
				/* Tone mark follows a valid syllable; move it in front, unless it's zero width. */
				.unsafeToBreakFromOutbuffer(, .idx)
				.nextGlyph()
				if !isZeroWidthChar(, ) {
					.mergeOutClusters(, +1)
					 := .outInfo
					 := []
					copy([+1:], [:])
					[] = 
				}
			} else {
				/* No valid syllable as base for tone mark; try to insert dotted circle. */
				if .Flags&DoNotinsertDottedCircle == 0 && .hasGlyph(0x25CC) {
					var  [2]rune
					if !isZeroWidthChar(, ) {
						[0] = 
						[1] = 0x25CC
					} else {
						[0] = 0x25CC
						[1] = 
					}
					.replaceGlyphs(1, [:], nil)
				} else {
					/* No dotted circle available in the font; just leave tone mark untouched. */
					.nextGlyph()
				}
			}
			 = len(.outInfo)
			 = len(.outInfo)
			continue
		}

		 = len(.outInfo) /* Remember current position as a potential syllable start;
		 * will only be used if we set end to a later position.
		 */

		if isL() && .idx+1 <  {
			 := 
			 := .cur(+1).codepoint
			if isV() {
				/* Have <L,V> or <L,V,T>. */
				var ,  rune
				if .idx+2 <  {
					 = .cur(+2).codepoint
					if isT() {
						 =  - ucd.HangulTBase /* Only used if isCombiningT (t); otherwise invalid. */
					} else {
						 = 0 /* The next character was not a trailing jamo. */
					}
				}
				 := 2
				if  != 0 {
					 = 3
				}
				.unsafeToBreak(.idx, .idx+)

				/* We've got a syllable <L,V,T?>; see if it can potentially be composed. */
				if (ucd.HangulLBase <=  &&  <= ucd.HangulLBase+ucd.HangulLCount-1) && (ucd.HangulVBase <=  &&  <= ucd.HangulVBase+ucd.HangulVCount-1) && ( == 0 || isCombiningT()) {
					/* Try to compose; if this succeeds, end is set to start+1. */
					 := ucd.HangulSBase + (-ucd.HangulLBase)*ucd.HangulNCount + (-ucd.HangulVBase)*ucd.HangulTCount + 
					if .hasGlyph() {
						.replaceGlyphs(, []rune{}, nil)
						 =  + 1
						continue
					}
				}

				/* We didn't compose, either because it's an Old Hangul syllable without a
				 * precomposed character in Unicode, or because the font didn't support the
				 * necessary precomposed glyph.
				 * Set jamo features on the individual glyphs, and advance past them.
				 */
				.cur(0).complexAux = ljmo
				.nextGlyph()
				.cur(0).complexAux = vjmo
				.nextGlyph()
				if  != 0 {
					.cur(0).complexAux = tjmo
					.nextGlyph()
					 =  + 3
				} else {
					 =  + 2
				}
				.mergeOutClusters(, )
				continue
			}
		} else if ucd.HangulSBase <=  &&  <= ucd.HangulSBase+ucd.HangulSCount-1 { // is combined S
			/* Have <LV>, <LVT>, or <LV,T> */
			 := 
			 := .hasGlyph()
			 := ( - ucd.HangulSBase) / ucd.HangulNCount
			 := ( - ucd.HangulSBase) % ucd.HangulNCount
			 :=  / ucd.HangulTCount
			 :=  % ucd.HangulTCount

			if  == 0 && .idx+1 <  && isCombiningT(.cur(+1).codepoint) {
				/* <LV,T>, try to combine. */
				 := .cur(+1).codepoint - ucd.HangulTBase
				 :=  + 
				if .hasGlyph() {
					.replaceGlyphs(2, []rune{}, nil)
					 =  + 1
					continue
				} else {
					.unsafeToBreak(.idx, .idx+2) /* Mark unsafe between LV and T. */
				}
			}

			/* Otherwise, decompose if font doesn't support <LV> or <LVT>,
			* or if having non-combining <LV,T>.  Note that we already handled
			* combining <LV,T> above. */
			if ! || ( == 0 && .idx+1 <  && isT(.cur(+1).codepoint)) {
				 := [3]rune{
					ucd.HangulLBase + ,
					ucd.HangulVBase + ,
					ucd.HangulTBase + ,
				}
				if .hasGlyph([0]) && .hasGlyph([1]) &&
					( == 0 || .hasGlyph([2])) {
					 := 2
					if  != 0 {
						 = 3
					}
					.replaceGlyphs(1, [:], nil)

					/* If we decomposed an LV because of a non-combining T following,
					* we want to include this T in the syllable.
					 */
					if  &&  == 0 {
						.nextGlyph()
						++
					}

					/* We decomposed S: apply jamo features to the individual glyphs
					* that are now in buffer.OutInfo.
					 */
					 := .outInfo
					 =  + 

					 := 
					[].complexAux = ljmo
					++
					[].complexAux = vjmo
					++
					if  <  {
						[].complexAux = tjmo
						++
					}

					.mergeOutClusters(, )
					continue
				} else if  == 0 && .idx+1 <  && isT(.cur(+1).codepoint) {
					.unsafeToBreak(.idx, .idx+2) /* Mark unsafe between LV and T. */
				}
			}

			if  {
				/* We didn't decompose the S, so just advance past it. */
				 =  + 1
				.nextGlyph()
				continue
			}
		}

		/* Didn't find a recognizable syllable, so we leave end <= start;
		 * this will prevent tone-mark reordering happening.
		 */
		.nextGlyph()
	}
	.swapBuffers()
}

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

	 := .Info
	for  := range  {
		[].Mask |= .maskArray[[].complexAux]
	}
}

func (complexShaperHangul) () (zeroWidthMarks, bool) {
	return zeroWidthMarksNone, true
}

func (complexShaperHangul) () normalizationMode {
	return nmNone
}

func (complexShaperHangul) () tables.Tag { return 0 }