package harfbuzz

import (
	

	
	
)

func ( *aatApplyContext) ( font.Kernx,  []kernxSubtableAccelerator) {
	var ,  bool

	.buffer.unsafeToConcat(0, maxInt)

	.setupBufferGlyphSet()

	for ,  := range  {
		var  bool

		if !.IsExtended && .IsVariation() {
			continue
		}

		if .buffer.Props.Direction.isHorizontal() != .IsHorizontal() {
			continue
		}

		.firstSet = [].first_set
		.secondSet = [].second_set
		.machineClassCache = [].class_cache

		if !.bufferIntersectsMachine() {
			if debugMode {
				fmt.Printf("AAT kerx : skipped subtable %d because no glyph matches\n", )
			}
			continue
		}

		 = .IsBackwards() != .buffer.Props.Direction.isBackward()

		if debugMode {
			fmt.Printf("AAT kerx : start subtable %d\n", )
		}

		if ! && .IsCrossStream() {
			/* Attach all glyphs into a chain. */
			 = true
			 := .buffer.Pos
			for  := range  {
				[].attachType = attachTypeCursive
				if .buffer.Props.Direction.isForward() {
					[].attachChain = -1
				} else {
					[].attachChain = +1
				}
				/* We intentionally don't set HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT,
				 * since there needs to be a non-zero attachment for post-positioning to
				 * be needed. */
			}
		}

		if  != .bufferIsReversed {
			.reverseBuffer()
		}

		 := .applyKerxSubtable()
		 =  || 

		if debugMode {
			fmt.Printf("AAT kerx : end subtable %d\n", )
			fmt.Println(.buffer.Pos)
		}
	}

	if .bufferIsReversed {
		.reverseBuffer()
	}
}

func ( *aatApplyContext) ( font.KernSubtable) bool {
	if debugMode {
		fmt.Printf("\tKERNX table %T\n", .Data)
	}
	switch data := .Data.(type) {
	case font.Kern0:
		if !.plan.requestedKerning {
			return false
		}
		if .IsBackwards() {
			return false
		}
		kern(kern0Accelerator{, }, .IsCrossStream(), .font, .buffer, .plan.kernMask, true)
	case font.Kern1:
		 := .IsCrossStream()
		if !.plan.requestedKerning && ! {
			return false
		}
		 := driverContextKerx1{c: , table: , crossStream: }
		 := newStateTableDriver(.Machine, .face)
		.drive(&, )
	case font.Kern2:
		if !.plan.requestedKerning {
			return false
		}
		if .IsBackwards() {
			return false
		}
		kern(kern2Accelerator{, }, .IsCrossStream(), .font, .buffer, .plan.kernMask, true)
	case font.Kern3:
		if !.plan.requestedKerning {
			return false
		}
		if .IsBackwards() {
			return false
		}
		kern(, .IsCrossStream(), .font, .buffer, .plan.kernMask, true)
	case font.Kern4:
		 := .IsCrossStream()
		if !.plan.requestedKerning && ! {
			return false
		}
		 := driverContextKerx4{c: , table: , actionType: .ActionType()}
		 := newStateTableDriver(.Machine, .face)
		.drive(&, )
	case font.Kern6:
		if !.plan.requestedKerning {
			return false
		}
		if .IsBackwards() {
			return false
		}
		kern(kern6Accelerator{, }, .IsCrossStream(), .font, .buffer, .plan.kernMask, true)
	}
	return true
}

// add a cache layout
type kern0Accelerator struct {
	table   font.Kern0
	context *aatApplyContext
}

func ( kern0Accelerator) (,  GID) int16 {
	if !.context.firstSet.hasGlyph() || !.context.secondSet.hasGlyph() {
		return 0
	}
	return .table.KernPair(, )
}

// Kernx1 state entry flags
const (
	kerx1Push        = 0x8000 // If set, push this glyph on the kerning stack.
	kerx1DontAdvance = 0x4000 // If set, don't advance to the next glyph before going to the new state.
	kerx1Reset       = 0x2000 // If set, reset the kerning data (clear the stack)
	kern1Offset      = 0x3FFF // Byte offset from beginning of subtable to the  value table for the glyphs on the kerning stack.
)

type driverContextKerx1 struct {
	c           *aatApplyContext
	table       font.Kern1
	stack       [8]int
	depth       int
	crossStream bool
}

func (driverContextKerx1) () bool { return true }

func ( driverContextKerx1) ( tables.AATStateEntry) bool {
	return .AsKernxIndex() != 0xFFFF
}

func ( *driverContextKerx1) ( *Buffer,  stateTableDriver,  tables.AATStateEntry) {
	 := .Flags

	if &kerx1Reset != 0 {
		.depth = 0
	}

	if &kerx1Push != 0 {
		if .depth < len(.stack) {
			.stack[.depth] = .idx
			.depth++
		} else {
			.depth = 0 /* Probably not what CoreText does, but better? */
		}
	}

	if .isActionable() && .depth != 0 {
		 := 1 // we do not support tupleCount > 0

		 := .AsKernxIndex()

		 := .table.Values[:]
		if len() < *.depth {
			.depth = 0
			return
		}

		 := .c.plan.kernMask

		/* From Apple 'kern' spec:
		 * "Each pops one glyph from the kerning stack and applies the kerning value to it.
		 * The end of the list is marked by an odd value... */
		var  bool
		for ! && .depth != 0 {
			.depth--
			 := .stack[.depth]
			 := [0]
			 = [:]
			if  >= len(.Pos) {
				continue
			}

			/* "The end of the list is marked by an odd value..." */
			 = &1 != 0
			 &= ^1

			 := &.Pos[]
			if .Props.Direction.isHorizontal() {
				if .crossStream {
					/* The following flag is undocumented in the spec, but described
					 * in the 'kern' table example. */
					if  == -0x8000 {
						.attachType = attachTypeNone
						.attachChain = 0
						.YOffset = 0
					} else if .attachType != 0 {
						.YOffset += .c.font.emScaleY()
						.scratchFlags |= bsfHasGPOSAttachment
					}
				} else if .Info[].Mask& != 0 {
					 := .c.font.emScaleX()
					.XAdvance += 
					.XOffset += 
				}
			} else {
				if .crossStream {
					/* CoreText doesn't do crossStream kerning in vertical.  We do. */
					if  == -0x8000 {
						.attachType = attachTypeNone
						.attachChain = 0
						.XOffset = 0
					} else if .attachType != 0 {
						.XOffset += .c.font.emScaleX()
						.scratchFlags |= bsfHasGPOSAttachment
					}
				} else if .Info[].Mask& != 0 {
					.YAdvance += .c.font.emScaleY()
					.YOffset += .c.font.emScaleY()
				}
			}
		}
	}
}

type kern2Accelerator struct {
	table   font.Kern2
	context *aatApplyContext
}

func ( kern2Accelerator) (,  GID) int16 {
	if !.context.firstSet.hasGlyph() || !.context.secondSet.hasGlyph() {
		return 0
	}
	return .table.KernPair(, )
}

type driverContextKerx4 struct {
	c          *aatApplyContext
	table      font.Kern4
	mark       int
	markSet    bool
	actionType uint8
}

func (driverContextKerx4) () bool { return true }

func (driverContextKerx4) ( tables.AATStateEntry) bool {
	return .AsKernxIndex() != 0xFFFF
}

func ( *driverContextKerx4) ( *Buffer,  stateTableDriver,  tables.AATStateEntry) {
	 := .AsKernxIndex()
	if .markSet &&  != 0xFFFF && .idx < len(.Pos) {
		 := .curPos(0)
		switch .actionType {
		case 0: /* Control Point Actions.*/
			/* Indexed into glyph outline. */
			 := .table.Anchors.(tables.KerxAnchorControls).Anchors[]

			, ,  := .c.font.getGlyphContourPointForOrigin(.c.buffer.Info[.mark].Glyph,
				.Mark, LeftToRight)
			, ,  := .c.font.getGlyphContourPointForOrigin(.c.buffer.cur(0).Glyph,
				.Current, LeftToRight)
			if ! || ! {
				return
			}

			.XOffset =  - 
			.YOffset =  - 

		case 1: /* Anchor Point Actions. */
			/* Indexed into 'ankr' table. */
			 := .table.Anchors.(tables.KerxAnchorAnchors).Anchors[]

			 := .c.ankrTable.GetAnchor(gID(.c.buffer.Info[.mark].Glyph), int(.Mark))
			 := .c.ankrTable.GetAnchor(gID(.c.buffer.cur(0).Glyph), int(.Current))

			.XOffset = .c.font.emScaleX(.X) - .c.font.emScaleX(.X)
			.YOffset = .c.font.emScaleY(.Y) - .c.font.emScaleY(.Y)

		case 2: /* Control Point Coordinate Actions. */
			 := .table.Anchors.(tables.KerxAnchorCoordinates).Anchors[]
			.XOffset = .c.font.emScaleX(.MarkX) - .c.font.emScaleX(.CurrentX)
			.YOffset = .c.font.emScaleY(.MarkY) - .c.font.emScaleY(.CurrentY)
		}
		.attachType = attachTypeMark
		.attachChain = int16(.mark - .idx)
		if .c.bufferIsReversed {
			.attachChain = -.attachChain
		}
		.scratchFlags |= bsfHasGPOSAttachment
	}

	const  = 0x8000 /* If set, remember this glyph as the marked glyph. */
	if .Flags& != 0 {
		.markSet = true
		.mark = .idx
	}
}

type kern6Accelerator struct {
	table   font.Kern6
	context *aatApplyContext
}

func ( kern6Accelerator) (,  GID) int16 {
	if !.context.firstSet.hasGlyph() || !.context.secondSet.hasGlyph() {
		return 0
	}
	return .table.KernPair(, )
}

type kernxSubtableAccelerator struct {
	first_set   intSet
	second_set  intSet
	class_cache aatClassCache
}

func newKernxSubtableAccelerator( font.KernSubtable) kernxSubtableAccelerator {
	var  kernxSubtableAccelerator
	.class_cache.clear()
	switch st := .Data.(type) {
	case font.Kern0:
		for ,  := range  {
			.first_set.add(uint32(.Left))
			.second_set.add(uint32(.Right))
		}
	case font.Kern1:
		collectLookupGlyphs(&.first_set, .Machine.Class)
		// collectLookupGlyphs (second_set, num_glyphs); // second_set is unused for machine kerning
	case font.Kern2:
		collectLookupGlyphs(&.first_set, .Left)
		collectLookupGlyphs(&.second_set, .Right)
	case font.Kern3:
		if  := len(.LeftClass);  != 0 {
			.first_set.addRange(0, uint32(-1))
			.second_set.addRange(0, uint32(-1))
		}
	case font.Kern4:
		collectLookupGlyphs(&.first_set, .Machine.Class)
		// collectLookupGlyphs (second_set, num_glyphs); // second_set is unused for machine kerning
	case font.Kern6:
		collectLookupGlyphs(&.first_set, .Row)
		collectLookupGlyphs(&.second_set, .Column)
	}
	return 
}