package harfbuzz

import (
	

	ucd 
)

// ported from harfbuzz/src/hb-ot-shape-fallback.cc Copyright © 2011,2012 Google, Inc. Behdad Esfahbod

const (
	combiningClassAttachedBelowLeft  = 200
	combiningClassAttachedBelow      = 202
	combiningClassAttachedAbove      = 214
	combiningClassAttachedAboveRight = 216
	combiningClassBelowLeft          = 218
	combiningClassBelow              = 220
	combiningClassBelowRight         = 222
	combiningClassLeft               = 224
	combiningClassRight              = 226
	combiningClassAboveLeft          = 228
	combiningClassAbove              = 230
	combiningClassAboveRight         = 232
	combiningClassDoubleBelow        = 233
	combiningClassDoubleAbove        = 234
)

func recategorizeCombiningClass( rune,  uint8) uint8 {
	if  >= 200 {
		return 
	}

	/* Thai / Lao need some per-character work. */
	if ( & ^0xFF) == 0x0E00 {
		if  == 0 {
			switch  {
			case 0x0E31, 0x0E34, 0x0E35, 0x0E36, 0x0E37, 0x0E47, 0x0E4C, 0x0E4D, 0x0E4E:
				 = combiningClassAboveRight
			case 0x0EB1, 0x0EB4, 0x0EB5, 0x0EB6, 0x0EB7, 0x0EBB, 0x0ECC, 0x0ECD:
				 = combiningClassAbove
			case 0x0EBC:
				 = combiningClassBelow
			}
		} else {
			/* Thai virama is below-right */
			if  == 0x0E3A {
				 = combiningClassBelowRight
			}
		}
	}

	switch  {

	/* Hebrew */
	case mcc10, /* sheva */
		mcc11, /* hataf segol */
		mcc12, /* hataf patah */
		mcc13, /* hataf qamats */
		mcc14, /* hiriq */
		mcc15, /* tsere */
		mcc16, /* segol */
		mcc17, /* patah */
		mcc18, /* qamats & qamats qatan */
		mcc20, /* qubuts */
		mcc22: /* meteg */
		return combiningClassBelow

	case mcc23: /* rafe */
		return combiningClassAttachedAbove

	case mcc24: /* shin dot */
		return combiningClassAboveRight

	case mcc25, /* sin dot */
		mcc19: /* holam & holam haser for vav*/
		return combiningClassAboveLeft

	case mcc26: /* point varika */
		return combiningClassAbove

	case mcc21: /* dagesh */

	/* Arabic and Syriac */

	case mcc27, /* fathatan */
		mcc28, /* dammatan */
		mcc30, /* fatha */
		mcc31, /* damma */
		mcc33, /* shadda */
		mcc34, /* sukun */
		mcc35, /* superscript alef */
		mcc36: /* superscript alaph */
		return combiningClassAbove

	case mcc29, /* kasratan */
		mcc32: /* kasra */
		return combiningClassBelow

	/* Thai */

	case mcc103: /* sara u / sara uu */
		return combiningClassBelowRight

	case mcc107: /* mai */
		return combiningClassAboveRight

	/* Lao */

	case mcc118: /* sign u / sign uu */
		return combiningClassBelow

	case mcc122: /* mai */
		return combiningClassAbove

	/* Tibetan */

	case mcc129: /* sign aa */
		return combiningClassBelow

	case mcc130: /* sign i*/
		return combiningClassAbove

	case mcc132: /* sign u */
		return combiningClassBelow

	}

	return 
}

func fallbackMarkPositionRecategorizeMarks( *Buffer) {
	for ,  := range .Info {
		if .unicode.generalCategory() == ucd.Mn {
			 := .getModifiedCombiningClass()
			 = recategorizeCombiningClass(.codepoint, )
			.Info[].setModifiedCombiningClass()
		}
	}
}

func zeroMarkAdvances( *Buffer, ,  int,  bool) {
	 := .Info
	for  := ;  < ; ++ {
		if [].unicode.generalCategory() != ucd.Mn {
			continue
		}
		if  {
			.Pos[].XOffset -= .Pos[].XAdvance
			.Pos[].YOffset -= .Pos[].YAdvance
		}
		.Pos[].XAdvance = 0
		.Pos[].YAdvance = 0
	}
}

func positionMark( *Font,  *Buffer,  *GlyphExtents,
	 int,  uint8,
) {
	,  := .GlyphExtents(.Info[].Glyph)
	if ! {
		return
	}

	 := .YScale / 16

	 := &.Pos[]
	.XOffset = 0
	.YOffset = 0

	// we don't position LEFT and RIGHT marks.

	// X positioning
	switch  {
	case combiningClassAttachedBelowLeft, combiningClassBelowLeft, combiningClassAboveLeft:
		/* Left align. */
		.XOffset += .XBearing - .XBearing

	case combiningClassAttachedAboveRight, combiningClassBelowRight, combiningClassAboveRight:
		/* Right align. */
		.XOffset += .XBearing + .Width - .Width - .XBearing
	case combiningClassDoubleBelow, combiningClassDoubleAbove:
		if .Props.Direction == LeftToRight {
			.XOffset += .XBearing + .Width - .Width/2 - .XBearing
			break
		} else if .Props.Direction == RightToLeft {
			.XOffset += .XBearing - .Width/2 - .XBearing
			break
		}
		fallthrough
	case combiningClassAttachedBelow, combiningClassAttachedAbove, combiningClassBelow, combiningClassAbove:
		fallthrough
	default:
		/* Center align. */
		.XOffset += .XBearing + (.Width-.Width)/2 - .XBearing
	}

	/* Y positioning */
	switch  {
	case combiningClassDoubleBelow, combiningClassBelowLeft, combiningClassBelow, combiningClassBelowRight:
		/* Add gap, fall-through. */
		.Height -= 
		fallthrough

	case combiningClassAttachedBelowLeft, combiningClassAttachedBelow:
		.YOffset = .YBearing + .Height - .YBearing
		/* Never shift up "below" marks. */
		if ( > 0) == (.YOffset > 0) {
			.Height -= .YOffset
			.YOffset = 0
		}
		.Height += .Height

	case combiningClassDoubleAbove, combiningClassAboveLeft, combiningClassAbove, combiningClassAboveRight:
		/* Add gap, fall-through. */
		.YBearing += 
		.Height -= 
		fallthrough
	case combiningClassAttachedAbove, combiningClassAttachedAboveRight:
		.YOffset = .YBearing - (.YBearing + .Height)
		/* Don't shift down "above" marks too much. */
		if ( > 0) != (.YOffset > 0) {
			 := -.YOffset / 2
			.YBearing += 
			.Height -= 
			.YOffset += 
		}
		.YBearing -= .Height
		.Height += .Height
	}
}

func positionAroundBase( *otShapePlan,  *Font,  *Buffer,
	,  int,  bool,
) {
	.unsafeToBreak(, )

	,  := .GlyphExtents(.Info[].Glyph)
	if ! {
		// if extents don't work, zero marks and go home.
		zeroMarkAdvances(, +1, , )
		return
	}
	.YBearing += .Pos[].YOffset
	/* Use horizontal advance for horizontal positioning.
	* Generally a better idea.  Also works for zero-ink glyphs.  See:
	* https://github.com/harfbuzz/harfbuzz/issues/1532 */
	.XBearing = 0
	.Width = .GlyphHAdvance(.Info[].Glyph)

	 := .Info[].getLigID()
	 := int32(.Info[].getLigNumComps())

	var ,  Position
	if .Props.Direction.isForward() {
		 -= .Pos[].XAdvance
		 -= .Pos[].YAdvance
	}

	var  Direction
	 := 
	 := int32(-1)
	 := uint8(255)
	 := 
	 := .Info
	for  :=  + 1;  < ; ++ {
		 := [].getModifiedCombiningClass()

		if  != 0 {
			if  > 1 {
				 := [].getLigID()
				 := int32([].getLigComp() - 1)
				// conditions for attaching to the last component.
				if  == 0 ||  !=  ||  >=  {
					 =  - 1
				}
				if  !=  {
					 = 
					 = 255
					 = 
					if  == 0 {
						if .props.Direction.isHorizontal() {
							 = .props.Direction
						} else {
							 = getHorizontalDirection(.props.Script)
						}
					}
					if  == LeftToRight {
						.XBearing += ( * .Width) / 
					} else {
						.XBearing += (( - 1 - ) * .Width) / 
					}
					.Width /= 
				}
			}

			if  !=  {
				 = 
				 = 
			}

			positionMark(, , &, , )

			.Pos[].XAdvance = 0
			.Pos[].YAdvance = 0
			.Pos[].XOffset += 
			.Pos[].YOffset += 

		} else {
			if .Props.Direction.isForward() {
				 -= .Pos[].XAdvance
				 -= .Pos[].YAdvance
			} else {
				 += .Pos[].XAdvance
				 += .Pos[].YAdvance
			}
		}
	}
}

func positionCluster( *otShapePlan,  *Font,  *Buffer,
	,  int,  bool,
) {
	if - < 2 {
		return
	}

	// find the base glyph
	 := .Info
	for  := ;  < ; ++ {
		if ![].isUnicodeMark() {
			// find mark glyphs
			var  int
			for  =  + 1;  < ; ++ {
				if [].isHidden() || [].isDefaultIgnorable() {
					continue
				}
				if ![].isUnicodeMark() {
					break
				}
			}

			positionAroundBase(, , , , , )

			 =  - 1
		}
	}
}

func fallbackMarkPosition( *otShapePlan,  *Font,  *Buffer,
	 bool,
) {
	var  int
	 := .Info
	for  := 1;  < len(); ++ {
		if ![].isUnicodeMark() && ![].isHidden() && ![].isDefaultIgnorable() {
			positionCluster(, , , , , )
			 = 
		}
	}
	positionCluster(, , , , len(), )
}

// adjusts width of various spaces.
func fallbackSpaces( *Font,  *Buffer) {
	if debugMode {
		fmt.Println("POSITION - applying fallback spaces")
	}
	 := .Info
	 := .Pos
	 := .Props.Direction.isHorizontal()
	for ,  := range  {
		if !.isUnicodeSpace() || .ligated() {
			continue
		}

		// If font had no ASCII space and we used the invisible glyph, give it a 1/4 EM default advance.
		if .Invisible != 0 && [].Glyph == .Invisible {
			if  {
				[].XAdvance = +.XScale / 4
			} else {
				[].YAdvance = -.YScale / 4
			}
		}

		 := .getUnicodeSpaceFallbackType()

		switch  {
		case notSpace, space: // shouldn't happen
		case spaceEM, spaceEM2, spaceEM3, spaceEM4, spaceEM5, spaceEM6, spaceEM16:
			if  {
				[].XAdvance = +(.XScale + int32()/2) / int32()
			} else {
				[].YAdvance = -(.YScale + int32()/2) / int32()
			}
		case space4EM18:
			if  {
				[].XAdvance = +.XScale * 4 / 18
			} else {
				[].YAdvance = -.YScale * 4 / 18
			}
		case spaceFigure:
			for  := '0';  <= '9'; ++ {
				if ,  := .face.NominalGlyph();  {
					if  {
						[].XAdvance = .GlyphHAdvance()
					} else {
						[].YAdvance = .getGlyphVAdvance()
					}
				}
			}
		case spacePunctuation:
			,  := .face.NominalGlyph('.')
			if ! {
				,  = .face.NominalGlyph(',')
			}
			if  {
				if  {
					[].XAdvance = .GlyphHAdvance()
				} else {
					[].YAdvance = .getGlyphVAdvance()
				}
			}
		case spaceNarrow:
			/* Half-space?
			* Unicode doc https://unicode.org/charts/PDF/U2000.pdf says ~1/4 or 1/5 of EM.
			* However, in my testing, many fonts have their regular space being about that
			* size.  To me, a percentage of the space width makes more sense.  Half is as
			* good as any. */
			if  {
				[].XAdvance /= 2
			} else {
				[].YAdvance /= 2
			}
		}
	}
}