// SPDX-License-Identifier: Unlicense OR BSD-3-Clause

package segmenter

import (
	ucd 
)

// -----------------------------------------------------------------------
// ------------------------- Grapheme boundaries -------------------------
// -----------------------------------------------------------------------

// Apply the Grapheme_Cluster_Boundary_Rules and returns a true if we are
// at a grapheme break.
// See https://unicode.org/reports/tr29/#Grapheme_Cluster_Boundary_Rules
func ( *cursor) () bool {
	 := .updateIndicConjunctBreakSequence() // apply rule GB9c
	 := .updatePictoSequence()              // apply rule GB11
	 := .updateGraphemeRIOdd()           // apply rule GB12 and GB13

	,  := .prevGrapheme, .grapheme
	if .r == '\n' && .prev == '\r' {
		return false // Rule GB3
	} else if &(ucd.GB_Control|ucd.GB_CR|ucd.GB_LF) != 0 ||
		&(ucd.GB_Control|ucd.GB_CR|ucd.GB_LF) != 0 {
		return true // Rules GB4 && GB5
	} else if  == ucd.GB_L && &(ucd.GB_L|ucd.GB_V|ucd.GB_LV|ucd.GB_LVT) != 0 { // rule GB6
		return false
	} else if &(ucd.GB_LV|ucd.GB_V) != 0 && &(ucd.GB_V|ucd.GB_T) != 0 {
		return false // rule GB7
	} else if &(ucd.GB_LVT|ucd.GB_T) != 0 &&  == ucd.GB_T {
		return false // rule GB8
	} else if &(ucd.GB_Extend|ucd.GB_ZWJ) != 0 {
		return false // Rule GB9
	} else if  == ucd.GB_SpacingMark {
		return false // Rule GB9a
	} else if  == ucd.GB_Prepend {
		return false // Rule GB9b
	} else if  {
		return false // Rule GB9c
	} else if  { // Rule GB11
		return false
	} else if  {
		return false // Rule GB12 && GB13
	}

	return true // Rule GB999
}

// update `isPrevGraphemeRIOdd` used for the rules GB12 and GB13
// and returns `true` if one of them triggered
func ( *cursor) () ( bool) {
	if .grapheme == ucd.GB_Regional_Indicator {
		 = .isPrevGraphemeRIOdd
		.isPrevGraphemeRIOdd = !.isPrevGraphemeRIOdd // switch the parity
	} else {
		.isPrevGraphemeRIOdd = false
	}
	return 
}

// see rule GB11
type pictoSequenceState uint8

const (
	noPictoSequence pictoSequenceState = iota // we are not in a sequence
	inPictoExtend                             // we are in (ExtendedPic)(Extend*) pattern
	seenPictoZWJ                              // we have seen (ExtendedPic)(Extend*)(ZWJ)
)

// update the `pictoSequence` state used for rule GB11 pattern :
// (ExtendedPic)(Extend*)(ZWJ)(ExtendedPic)
// and returns true if we matched one
func ( *cursor) () bool {
	switch .pictoSequence {
	case noPictoSequence:
		// we are not in a sequence yet, start it if we have an ExtendedPic
		if .isExtentedPic {
			.pictoSequence = inPictoExtend
		}
		return false
	case inPictoExtend:
		if .grapheme == ucd.GB_Extend {
			// continue the sequence with an Extend rune
		} else if .grapheme == ucd.GB_ZWJ {
			// close the variable part of the sequence with (ZWJ)
			.pictoSequence = seenPictoZWJ
		} else {
			// stop the sequence
			.pictoSequence = noPictoSequence
		}
		return false
	case seenPictoZWJ:
		// trigger GB11 if we have an ExtendedPic,
		// and reset the sequence
		if .isExtentedPic {
			.pictoSequence = inPictoExtend
			return true
		}
		.pictoSequence = noPictoSequence
		return false
	default:
		panic("exhaustive switch")
	}
}

// see rule GB9c
type indicCBSequenceState uint8

const (
	noIndicCBSequence   indicCBSequenceState = iota // we are not in a sequence
	inIndicCBSequence                               // we are in (Consonant) (Extend Linker)* pattern
	seenIndicCBSequence                             // we have seen (Consonant) (Extend Linker)* (Linker) (Extend Linker)*
)

// update the `indicConjunctBreakSequence` state used for rule CB9c pattern :
// (Consonant) (Extend Linker)* (Linker) (Extend Linker)* (Consonant)
// and returns true if we matched one
func ( *cursor) () bool {
	 := .indicConjunctBreak
	switch .indicConjunctBreakSequence {
	case noIndicCBSequence:
		// we are not in a sequence yet, start it if we have a Consonant
		if  == ucd.ICBConsonant {
			.indicConjunctBreakSequence = inIndicCBSequence
		}
		return false
	case inIndicCBSequence:
		if  == ucd.ICBExtend {
			// continue the sequence
		} else if  == ucd.ICBLinker {
			// we now have at least on Linker
			.indicConjunctBreakSequence = seenIndicCBSequence
		} else if  == ucd.ICBConsonant {
			// reset the sequence
		} else {
			// stop the sequence
			.indicConjunctBreakSequence = noIndicCBSequence
		}
		return false
	case seenIndicCBSequence:
		if &(ucd.ICBExtend|ucd.ICBLinker) != 0 {
			// continue the sequence
			return false
		} else if  == ucd.ICBConsonant {
			// start a new sequence
			.indicConjunctBreakSequence = inIndicCBSequence
			return true
		} else {
			// stop the sequence
			.indicConjunctBreakSequence = noIndicCBSequence
			return false
		}
	default:
		panic("exhaustive switch")
	}
}

// -----------------------------------------------------------------------
// ------------------------- Word boundaries -----------------------------
// -----------------------------------------------------------------------

// update `isPrevWordRIOdd` used for the rules WB15 and WB16
// and returns `true` if one of them triggered
func ( *cursor) () ( bool) {
	if .word == ucd.WB_ExtendFormat {
		return false // skip
	}

	if .word == ucd.WB_Regional_Indicator {
		 = .isPrevWordRIOdd
		.isPrevWordRIOdd = !.isPrevWordRIOdd // switch the parity
	} else {
		.isPrevWordRIOdd = false
	}
	return 
}

// Apply the Word_Boundary_Rules and returns true if we are at a
// word boundary.
// removePrevNoExtend is true if the index [prevWordNoExtend]
// should be marked as NOT being a word boundary
// See https://unicode.org/reports/tr29/#Word_Boundary_Rules
func ( *cursor) ( int) (,  bool) {
	 := .updateWordRIOdd()

	, ,  := .prevPrevWord, .prevWord, .word

	// we apply Rules WB1 and WB2 at the end of the main loop

	 := .prevWordNoExtend == -1

	if .prev == '\u000D' && .r == '\u000A' { // Rule WB3
		 = false
	} else if  == ucd.WB_NewlineCRLF &&  {
		// The extra check for prevWordNoExtend is to correctly handle sequences like
		// Newline ÷ Extend × Extend
		// since we have not skipped ExtendFormat yet.
		 = true // Rule WB3a
	} else if  == ucd.WB_NewlineCRLF {
		 = true // Rule WB3b
	} else if .prev == 0x200D && .isExtentedPic {
		 = false // Rule WB3c
	} else if  == ucd.WB_WSegSpace &&
		 == ucd.WB_WSegSpace &&  {
		 = false // Rule WB3d
	} else if  == ucd.WB_ExtendFormat {
		 = false // Rules WB4
	} else if &(ucd.WB_ALetter|ucd.WB_Hebrew_Letter|ucd.WB_Numeric) != 0 &&
		&(ucd.WB_ALetter|ucd.WB_Hebrew_Letter|ucd.WB_Numeric) != 0 {
		 = false // Rules WB5, WB8, WB9, WB10
	} else if  == ucd.WB_Katakana &&  == ucd.WB_Katakana {
		 = false // Rule WB13
	} else if &(ucd.WB_ALetter|ucd.WB_Hebrew_Letter|ucd.WB_Numeric|ucd.WB_Katakana|ucd.WB_ExtendNumLet) != 0 &&
		 == ucd.WB_ExtendNumLet {
		 = false // Rule WB13a
	} else if  == ucd.WB_ExtendNumLet &&
		&(ucd.WB_ALetter|ucd.WB_Hebrew_Letter|ucd.WB_Numeric|ucd.WB_Katakana) != 0 {
		 = false // Rule WB13b
	} else if &(ucd.WB_ALetter|ucd.WB_Hebrew_Letter) != 0 &&
		&(ucd.WB_MidLetter|ucd.WB_MidNumLet|ucd.WB_Single_Quote) != 0 &&
		&(ucd.WB_ALetter|ucd.WB_Hebrew_Letter) != 0 {
		 = true // Rule WB6
		 = false    // Rule WB7
	} else if  == ucd.WB_Hebrew_Letter &&  == ucd.WB_Single_Quote {
		 = false // Rule WB7a
	} else if  == ucd.WB_Hebrew_Letter && .prev == 0x0022 &&
		 == ucd.WB_Hebrew_Letter {
		 = true // Rule WB7b
		 = false    // Rule WB7c
	} else if ( == ucd.WB_Numeric &&  == ucd.WB_Numeric) &&
		&(ucd.WB_MidNum|ucd.WB_MidNumLet|ucd.WB_Single_Quote) != 0 {
		 = false    // Rule WB11
		 = true // Rule WB12
	} else if  {
		 = false // Rule WB15 and WB16
	} else {
		 = true // Rule WB999
	}

	return , 
}