package harfbuzz

import (
	
	
)

/* ported from harfbuzz/src/hb-buffer.hh and hb-buffer.h
 * Copyright © 1998-2004  David Turner and Werner Lemberg
 * Copyright © 2004,2007,2009,2010  Red Hat, Inc.
 * Copyright © 2011,2012  Google, Inc.
 * Red Hat Author(s): Owen Taylor, Behdad Esfahbod
 * Google Author(s): Behdad Esfahbod */

const (
	// The following are used internally; not derived from GDEF.
	substituted tables.GlyphProps = 1 << (iota + 4)
	ligated
	multiplied

	preserve = substituted | ligated | multiplied
)

type bufferScratchFlags uint32

const (
	bsfHasFractionSlash bufferScratchFlags = 1 << iota
	bsfHasDefaultIgnorables
	bsfHasSpaceFallback
	bsfHasGPOSAttachment
	bsfHasCGJ
	bsfHasBrokenSyllable
	bsfHasVariationSelectorFallback
	bsfHasContinuations

	bsfDefault bufferScratchFlags = 0x00000000

	// reserved for shapers' internal use.
	bsfShaper0 bufferScratchFlags = 0x01000000
	bsfShaper1 bufferScratchFlags = 0x02000000
	bsfShaper2 bufferScratchFlags = 0x04000000
	bsfShaper3 bufferScratchFlags = 0x08000000
)

// maximum length of additional context added outside
// input text
const contextLength = 5

const (
	maxOpsDefault = 0x1FFFFFFF
	maxLenDefault = 0x3FFFFFFF
)

// Buffer is the main structure holding the input text segment and its properties before shaping,
// and output glyphs and their information after shaping.
type Buffer struct {
	// Info is used as internal storage during the shaping,
	// and also exposes the result: the glyph to display
	// and its original Cluster value.
	Info []GlyphInfo

	// Pos gives the position of the glyphs resulting from the shapping
	// It has the same length has `Info`.
	Pos []GlyphPosition

	// Text before / after the main buffer contents, ordered outward !
	// Index 0 is for "pre-Context", 1 for "post-Context".
	context [2][]rune

	// temporary storage, usully used the following way:
	// 	- truncate the slice
	//	- walk through Info and append glyphs to outInfo
	//	- swap back Into and outInfo with 'swapBuffers'
	outInfo []GlyphInfo

	// Props is required to correctly interpret the input runes.
	Props SegmentProperties
	// Glyph that replaces invisible characters in
	// the shaping result. If set to zero (default), the glyph for the
	// U+0020 SPACE character is used. Otherwise, this value is used
	// verbatim.
	Invisible GID

	// Glyph that replaces characters not found in the font during shaping.
	// The not-found glyph defaults to zero, sometimes known as the
	// ".notdef" glyph.
	NotFound                  GID
	notFoundVariationSelector GID

	// Information about how the text in the buffer should be treated.
	Flags ShappingOptions
	// Precise the cluster handling behavior.
	ClusterLevel ClusterLevel

	// some pathological cases can be constructed
	// (for example with GSUB tables), where the size of the buffer
	// grows out of bounds
	// these bounds avoid such cases, which should never happen with
	// decent font files
	maxOps int // maximum operations allowed
	maxLen int // maximum length allowed

	serial       uint
	idx          int                // Cursor into `info` and `pos` arrays
	scratchFlags bufferScratchFlags // Have space-fallback, etc.

	haveOutput bool

	planCache map[Face][]*shapePlan

	digest setDigest
}

// NewBuffer allocate a storage with default options.
// It should then be populated with `AddRunes` and shapped with `Shape`.
func () *Buffer {
	return &Buffer{
		ClusterLevel:              MonotoneGraphemes,
		maxOps:                    maxOpsDefault,
		planCache:                 map[Face][]*shapePlan{},
		notFoundVariationSelector: 0xFFFFFFFF,
	}
}

// AddRune appends a character with the Unicode value of `codepoint` to `b`, and
// gives it the initial cluster value of `cluster`. Clusters can be any thing
// the client wants, they are usually used to refer to the index of the
// character in the input text stream and are output in the
// `GlyphInfo.Cluster` field.
// This also clears the posterior context (see `AddRunes`).
func ( *Buffer) ( rune,  int) {
	.append(, )
	.clearContext(1)
}

func ( *Buffer) ( rune,  int) {
	.Info = append(.Info, GlyphInfo{codepoint: , Cluster: })
	.Pos = append(.Pos, GlyphPosition{})
}

// AddRunes appends characters from `text` array to `b`. `itemOffset` is the
// position of the first character from `text` that will be appended, and
// `itemLength` is the number of character to add (-1 means the end of the slice).
// When shaping part of a larger text (e.g. a run of text from a paragraph),
// instead of passing just the substring
// corresponding to the run, it is preferable to pass the whole
// paragraph and specify the run start and length as `itemOffset` and
// `itemLength`, respectively, to give HarfBuzz the full context to be able,
// for example, to do cross-run Arabic shaping or properly handle combining
// marks at start of run.
// The cluster value attributed to each rune is the index in the `text` slice.
func ( *Buffer) ( []rune, ,  int) {
	/* If buffer is empty and pre-context provided, install it.
	* This check is written this way, to make sure people can
	* provide pre-context in one add_utf() call, then provide
	* text in a follow-up call.  See:
	*
	* https://bugzilla.mozilla.org/show_bug.cgi?id=801410#c13 */
	if len(.Info) == 0 &&  > 0 {
		// add pre-context
		.clearContext(0)
		for  :=  - 1;  >= 0 && len(.context[0]) < contextLength; -- {
			.context[0] = append(.context[0], [])
		}
	}

	if  < 0 {
		 = len() - 
	}

	for ,  := range [ : +] {
		.append(, +)
	}

	// add post-context
	 :=  +  + contextLength
	if  > len() {
		 = len()
	}
	.context[1] = [+ : ]
}

// GuessSegmentProperties fills unset buffer segment properties based on buffer Unicode
// contents and can be used when no other information is available.
//
// If buffer `Props.Script` is zero, it
// will be set to the Unicode script of the first character in
// the buffer that has a script other than Common,
// Inherited, and Unknown.
//
// Next, if buffer `Props.Direction` is zero,
// it will be set to the natural horizontal direction of the
// buffer script, defaulting to `LeftToRight`.
//
// Finally, if buffer Props.Language is empty,
// it will be set to the process's default language.
func ( *Buffer) () {
	/* If script is not set, guess from buffer contents */
	if .Props.Script == 0 {
		for ,  := range .Info {
			 := language.LookupScript(.codepoint)
			if .Strong() &&  != language.Unknown {
				.Props.Script = 
				break
			}
		}
	}

	/* If direction is unset, guess from script */
	if .Props.Direction == 0 {
		.Props.Direction = getHorizontalDirection(.Props.Script)
		if .Props.Direction == 0 {
			.Props.Direction = LeftToRight
		}
	}

	/* If language is not set, use default language from locale */
	if .Props.Language == "" {
		.Props.Language = language.DefaultLanguage()
	}
}

// Clear resets `b` to its initial empty state (including user settings).
// This method should be used to reuse the allocated memory.
func ( *Buffer) () {
	.ClusterLevel = 0
	.Flags = 0
	.Invisible = 0
	.NotFound = 0
	.notFoundVariationSelector = 0xFFFFFFFF

	.Props = SegmentProperties{}
	.scratchFlags = 0

	.haveOutput = false

	.idx = 0
	.Info = .Info[:0]
	.outInfo = .outInfo[:0]
	.Pos = .Pos[:0]
	.clearContext(0)
	.clearContext(1)

	.serial = 0
}

// cur returns the glyph at the cursor, optionaly shifted by `i`.
// Its simply a syntactic sugar for `&b.Info[b.idx+i] `
func ( *Buffer) ( int) *GlyphInfo { return &.Info[.idx+] }

// cur returns the position at the cursor, optionaly shifted by `i`.
// Its simply a syntactic sugar for `&b.Pos[b.idx+i]
func ( *Buffer) ( int) *GlyphPosition { return &.Pos[.idx+] }

// returns the last glyph of `outInfo`
func ( *Buffer) () *GlyphInfo {
	return &.outInfo[len(.outInfo)-1]
}

func ( *Buffer) () {
	.digest = setDigest{}
	for ,  := range .Info {
		.digest.add(gID(.Glyph))
	}
}

// func (b Buffer) has_separate_output() bool { return info != b.outInfo }

func ( *Buffer) () int {
	if .haveOutput {
		return len(.outInfo)
	}
	return .idx
}

func ( *Buffer) () int { return len(.Info) - .idx }

// func (b *Buffer) nextSerial() uint {
// 	out := b.serial
// 	b.serial++
// 	return out
// }

// Copies glyph at `idx` to `outInfo` before replacing its codepoint by `u`
// Advances `idx`
func ( *Buffer) ( rune) {
	.replaceGlyphs(1, []rune{}, nil)
}

// Copies glyph at `idx` to `outInfo` before replacing its codepoint by `u`
// Advances `idx`
func ( *Buffer) ( GID) {
	.outInfo = append(.outInfo, .Info[.idx])
	.outInfo[len(.outInfo)-1].Glyph = 
	.idx++
}

// Merges clusters in [idx:idx+numIn], then duplicate `Info[idx]` len(codepoints) times to `outInfo`.
// Advances `idx` by `numIn`. Assume that idx + numIn <= len(Info)
// Also replaces their codepoint by `codepoints` and their glyph by `glyphs` if non nil
func ( *Buffer) ( int,  []rune,  []GID) {
	.mergeClusters(.idx, .idx+)

	var  *GlyphInfo
	if .idx < len(.Info) {
		 = .cur(0)
	} else {
		 = .prev()
	}
	 :=  != nil
	 :=  != nil
	 := len(.outInfo)

	 := max(len(), len())
	.outInfo = append(.outInfo, make([]GlyphInfo, )...)
	for  := 0;  < ; ++ {
		.outInfo[+] = *
		if  {
			.outInfo[+].codepoint = []
		}
		if  {
			.outInfo[+].Glyph = []
		}
	}

	.idx += 
}

// makes a copy of the glyph at idx to output and replace in output `codepoint`
// by `r`. Does NOT adavance `idx`
func ( *Buffer) ( rune) {
	.replaceGlyphs(0, []rune{}, nil)
}

// same as outputRune
func ( *Buffer) ( GID) {
	.replaceGlyphs(0, nil, []GID{})
}

// Copies glyph at idx to output but doesn't advance idx
func ( *Buffer) () {
	.outInfo = append(.outInfo, .Info[.idx])
}

// Copies glyph at `idx` to `outInfo` and advance `idx`.
// If there's no output, just advance `idx`.
func ( *Buffer) () {
	if .haveOutput {
		.outInfo = append(.outInfo, .Info[.idx])
	}

	.idx++
}

// Copies `n` glyphs from `idx` to `outInfo` and advances `idx`.
// If there's no output, just advance idx.
func ( *Buffer) ( int) {
	if .haveOutput {
		.outInfo = append(.outInfo, .Info[.idx:.idx+]...)
	}
	.idx += 
}

// skipGlyph advances idx without copying to output
func ( *Buffer) () { .idx++ }

func ( *Buffer) ( GlyphMask) {
	for  := range .Info {
		.Info[].Mask = 
	}
}

// Adds glyph flags in mask to infos with clusters between start and end.
// The start index will be from out-buffer if [fromOutBuffer] is true.
// If [interior] is true, then the cluster having the minimum value is skipped.
func ( *Buffer) ( GlyphMask, ,  int, ,  bool) {
	if  != maxInt && - > 255 {
		return
	}

	 := .Info
	 = min(, len())

	if  && ! && - < 2 {
		return
	}

	if ! || !.haveOutput {
		if ! {
			for  := ;  < ; ++ {
				[].Mask |= 
			}
		} else {
			 := .findMinCluster(, , , maxInt)
			.infosSetGlyphFlags(, , , , )
		}
	} else {
		// assert (start <= out_len);
		// assert (idx <= end);
		 := .outInfo
		if ! {
			for  := ;  < len(); ++ {
				[].Mask |= 
			}
			for  := .idx;  < ; ++ {
				[].Mask |= 
			}
		} else {
			 := .findMinCluster(, .idx, , maxInt)
			 = .findMinCluster(, , len(), )

			.infosSetGlyphFlags(, , len(), , )
			.infosSetGlyphFlags(, .idx, , , )
		}
	}
}

func ( *Buffer) (,  GlyphMask, ,  int) {
	if  == 0 {
		return
	}
	 := ^
	 &= 

	.maxOps -= len(.Info)

	if  == 0 &&  == -1 {
		for ,  := range .Info {
			.Info[].Mask = (.Mask & ) | 
		}
		return
	}

	for ,  := range .Info {
		if  <= .Cluster && .Cluster <  {
			.Info[].Mask = (.Mask & ) | 
		}
	}
}

func ( *Buffer) (,  int) {
	if - < 2 {
		return
	}

	if .ClusterLevel == Characters {
		.unsafeToBreak(, )
		return
	}

	.maxOps -=  - 

	 := .Info[].Cluster

	for  :=  + 1;  < ; ++ {
		 = min(, .Info[].Cluster)
	}

	// Extend end
	if  != .Info[-1].Cluster {
		for  < len(.Info) && .Info[-1].Cluster == .Info[].Cluster {
			++
		}
	}

	// Extend start
	if  != .Info[].Cluster {
		for .idx <  && .Info[-1].Cluster == .Info[].Cluster {
			--
		}
	}

	// If we hit the start of buffer, continue in out-buffer.
	if .idx ==  && .Info[].Cluster !=  {
		 := .Info[].Cluster
		for  := len(.outInfo);  != 0 && .outInfo[-1].Cluster == ; -- {
			.outInfo[-1].setCluster(, 0)
		}
	}

	for  := ;  < ; ++ {
		.Info[].setCluster(, 0)
	}
}

// merge clusters for deleting current glyph, and skip it.
func ( *Buffer) () {
	// The logic here is duplicated in hb_ot_hide_default_ignorables().

	 := .Info[.idx].Cluster
	if  := len(.outInfo); .idx+1 < len(.Info) &&  == .Info[.idx+1].Cluster ||
		 != 0 &&  == .outInfo[-1].Cluster {
		/* Cluster survives; do nothing. */
		goto 
	}

	if  := len(.outInfo);  != 0 {
		/* Merge cluster backward. */
		if  < .outInfo[-1].Cluster {
			 := .Info[.idx].Mask
			 := .outInfo[-1].Cluster
			for  := ;  != 0 && .outInfo[-1].Cluster == ; -- {
				.outInfo[-1].setCluster(, )
			}
		}
		goto 
	}

	if .idx+1 < len(.Info) {
		/* Merge cluster forward. */
		.mergeClusters(.idx, .idx+2)
		goto 
	}

:
	.skipGlyph()
}

func ( *Buffer) ( func(*GlyphInfo) bool) {
	// Merge clusters and delete filtered glyphs.
	// NOTE! We can't use out-buffer as we have positioning data.
	var (
		    int
		 = .Info
		  = .Pos
	)
	for  := range  {
		if (&[]) {
			/* Merge clusters.
			* Same logic as buffer.deleteGlyph(), but for in-place removal. */

			 := [].Cluster
			if +1 < len(.Info) &&  == [+1].Cluster {
				// Cluster survives; do nothing.
				continue
			}

			if  != 0 {
				// Merge cluster backward.
				if  < [-1].Cluster {
					 := [].Mask
					 := [-1].Cluster
					for  := ;  != 0 && [-1].Cluster == ; -- {
						[-1].setCluster(, )
					}
				}
				continue
			}

			if +1 < len(.Info) {
				// Merge cluster forward.
				.mergeClusters(, +2)
			}

			continue
		}

		if  !=  {
			[] = []
			[] = []
		}
		++
	}
	.Info = .Info[:]
	.Pos = .Pos[:]
}

// unsafeToBreak adds the flag `GlyphFlagUnsafeToBreak`
// when needed, between `start` and `end`.
func ( *Buffer) (,  int) {
	.setGlyphFlags(GlyphUnsafeToBreak|GlyphUnsafeToConcat, , , true, false)
}

func ( *Buffer) (,  int) {
	if (.Flags & ProduceSafeToInsertTatweel) == 0 {
		.unsafeToBreak(, )
		return
	}
	.setGlyphFlags(GlyphSafeToInsertTatweel, , , true, false)
}

// start = 0, end = maxInt
func ( *Buffer) (,  int) {
	if (.Flags & ProduceUnsafeToConcat) == 0 {
		return
	}
	.setGlyphFlags(GlyphUnsafeToConcat, , , true, false)
}

func ( *Buffer) (,  int) {
	.setGlyphFlags(GlyphUnsafeToBreak|GlyphUnsafeToConcat, , , true, true)
}

func ( *Buffer) (,  int) {
	if (.Flags & ProduceUnsafeToConcat) == 0 {
		return
	}
	.setGlyphFlags(GlyphUnsafeToConcat, , , false, true)
}

// return the smallest cluster between `cluster` and  infos[start:end]
func ( *Buffer) ( []GlyphInfo, , ,  int) int {
	if  ==  {
		return 
	}
	if .ClusterLevel == Characters {
		for  := ;  < ; ++ {
			 = min(, [].Cluster)
		}
		return 
	}
	return min(, min([].Cluster, [-1].Cluster))
}

func ( *Buffer) ( []GlyphInfo, , ,  int,  GlyphMask) {
	if  ==  {
		return
	}

	.maxOps -=  - 

	 := [].Cluster
	 := [-1].Cluster

	if .ClusterLevel == Characters || ( !=  &&  != ) {
		for  := ;  < ; ++ {
			if  != [].Cluster {
				[].Mask |= 
			}
		}
		return
	}

	/* Monotone clusters */

	if  ==  {
		for  := ;  <  && [-1].Cluster != ; -- {
			[-1].Mask |= 
		}
	} else /* cluster == clusterLast */ {
		for  := ;  <  && [].Cluster != ; ++ {
			[].Mask |= 
		}
	}
}

// reset `b.outInfo`, and adjust `pos` to have
// same length as `Info` (without zeroing its values)
func ( *Buffer) () {
	.haveOutput = false
	// b.have_positions = true

	.outInfo = .outInfo[:0]

	 := len(.Info)
	if cap(.Pos) >=  {
		.Pos = .Pos[:]
	} else {
		.Pos = make([]GlyphPosition, )
	}
}

// truncate `outInfo` and set `haveOutput`
func ( *Buffer) ( bool) {
	.haveOutput = 
	// b.have_positions = false

	.outInfo = .outInfo[:0]
}

// truncate `outInfo` and set `haveOutput` to true
func ( *Buffer) () {
	.removeOutput(true)
	.idx = 0
}

func ( *Buffer) ( uint) { .context[] = .context[][:0] }

// reverses the subslice [start:end] of the buffer contents
func ( *Buffer) (,  int) {
	if - < 2 {
		return
	}
	 := .Info[:]
	 := .Pos[:]
	 := len()
	_ = [-1] // BCE
	for  := /2 - 1;  >= 0; -- {
		 :=  - 1 - 
		[], [] = [], []
		[], [] = [], [] // same length
	}
}

// Reverse reverses buffer contents, that is the order of the `Info` and `Pos` slices.
func ( *Buffer) () { .reverseRange(0, len(.Info)) }

func ( *Buffer) () {
	.reverseGroups(func(,  *GlyphInfo) bool {
		return .Cluster == .Cluster
	}, false)
}

// mergeClusters = false
func ( *Buffer) ( func(*GlyphInfo, *GlyphInfo) bool,  bool) {
	if len(.Info) == 0 {
		return
	}

	 := len(.Info)
	 := 0
	var  int
	for  = 1;  < ; ++ {
		if !(&.Info[-1], &.Info[]) {
			if  {
				.mergeClusters(, )
			}
			.reverseRange(, )
			 = 
		}
	}

	if  {
		.mergeClusters(, )
	}
	.reverseRange(, )

	.Reverse()
}

// swap back the temporary outInfo buffer to `Info`
// and resets the cursor `idx`.
// Assume that haveOutput is true, and toogle it.
func ( *Buffer) () {
	.nextGlyphs(len(.Info) - .idx)
	.haveOutput = false
	.Info, .outInfo = .outInfo, .Info
	.idx = 0
}

// returns an unique id
func ( *Buffer) () uint8 {
	 := uint8(.serial & 0x07)
	.serial++
	if  == 0 { // in case of overflow
		 = .()
	}
	return 
}

// return false if maximum ops limit is reached
func ( *Buffer) ( int) bool {
	//   assert (have_output);
	 := len(.Info)
	.maxOps -=  - .idx
	if .maxOps < 0 {
		return false
	}
	.Info = append(.Info, make([]GlyphInfo, )...)
	copy(.Info[.idx+:], .Info[.idx:])
	.idx += 
	return true
}

func ( *Buffer) ( int) {
	if !.haveOutput {
		// assert(i <= len)
		.idx = 
		return
	}

	// assert(i <= out_len+(len-idx))
	 := len(.outInfo)
	if  <  {
		 :=  - 
		.outInfo = append(.outInfo, .Info[.idx:+.idx]...)
		.idx += 
	} else if  >  {
		/* Tricky part: rewinding... */
		 :=  - 

		if .idx <  {
			 := .shiftForward( - .idx)
			if ! {
				return
			}
		}

		// assert(idx >= count)

		.idx -= 
		copy(.Info[.idx:], .outInfo[-:])
		.outInfo = .outInfo[:-]
	}
}

func ( *Buffer) ( *intSet) {
	.Clear()
	for ,  := range .Info {
		.addGlyph(.Glyph)
	}
}

// iterator over the grapheme of a buffer
type graphemesIterator struct {
	buffer *Buffer
	start  int
}

// at the end of the buffer, start >= len(info)
func ( *graphemesIterator) () (,  int) {
	 := .buffer.Info
	 := len()
	 = .start
	if  >=  {
		return
	}
	for  = .start + 1;  <  && [].isContinuation(); ++ {
	}
	.start = 
	return , 
}

func ( *Buffer) () (*graphemesIterator, int) {
	return &graphemesIterator{buffer: }, len(.Info)
}

// iterator over clusters of a buffer with the loop
// for start, end := iter.Next(); start < count; start, end = iter.Next() {}
type clusterIterator struct {
	buffer *Buffer
	start  int
}

// returns the next cluster delimited by [start, end[
func ( *clusterIterator) () (,  int) {
	 := .buffer.Info
	 := len()
	 = .start
	if  >=  {
		return
	}
	 := [].Cluster
	for  =  + 1;  <  &&  == [].Cluster; ++ {
	}
	.start = 

	return , 
}

func ( *Buffer) () (*clusterIterator, int) {
	return &clusterIterator{buffer: }, len(.Info)
}

// iterator over syllables of a buffer
type syllableIterator struct {
	buffer *Buffer
	start  int
}

func ( *syllableIterator) () (,  int) {
	 := .buffer.Info
	 := len(.buffer.Info)
	 = .start
	if  >=  {
		return
	}
	 := [].syllable
	for  =  + 1;  <  &&  == [].syllable; ++ {
	}
	.start = 
	return , 
}

func ( *Buffer) () (*syllableIterator, int) {
	return &syllableIterator{buffer: }, len(.Info)
}

// only modifies Info, thus assume Pos is not used yet
func ( *Buffer) (,  int,  func(,  *GlyphInfo) int) {
	for  :=  + 1;  < ; ++ {
		 := 
		for  >  && (&.Info[-1], &.Info[]) > 0 {
			--
		}
		if  ==  {
			continue
		}
		// move item i to occupy place for item j, shift what's in between.
		.mergeClusters(, +1)

		 := .Info[]
		copy(.Info[+1:], .Info[:])
		.Info[] = 
	}
}

func ( *Buffer) (,  int) {
	if .ClusterLevel == Characters {
		return
	}

	if - < 2 {
		return
	}

	 := .outInfo[].Cluster

	for  :=  + 1;  < ; ++ {
		 = min(, .outInfo[].Cluster)
	}

	/* Extend start */
	for  != 0 && .outInfo[-1].Cluster == .outInfo[].Cluster {
		--
	}

	/* Extend end */
	for  < len(.outInfo) && .outInfo[-1].Cluster == .outInfo[].Cluster {
		++
	}

	/* If we hit the end of out-buffer, continue in buffer. */
	if  == len(.outInfo) {
		 := .outInfo[-1].Cluster
		for  := .idx;  < len(.Info) && .Info[].Cluster == ; ++ {
			.Info[].setCluster(, 0)
		}
	}

	for  := ;  < ; ++ {
		.outInfo[].setCluster(, 0)
	}
}