package harfbuzz

import (
	
	
)

// ported from src/hb-font.hh, src/hb-font.cc  Copyright © 2009  Red Hat, Inc., 2012  Google, Inc.  Behdad Esfahbod

type Face = *font.Face

// Font is used internally as a wrapper around the provided Face.
//
// Font are constructed with `NewFont` and adjusted by accessing the fields
// Ptem, XScale, YScale.
//
// Fonts private fields only depend on the provided [*font.Font], so a Font object is suitable for caching.
type Font struct {
	face Face

	gsubAccels, gposAccels []otLayoutLookupAccelerator // accelators for lookup
	morxAccels             [][]morxSubtableAccelerator
	kernAccels, kerxAccels []kernxSubtableAccelerator

	faceUpem int32 // cached value of Face.Upem()

	// Point size of the font. Set to zero to unset.
	// This is used in AAT layout, when applying 'trak' table.
	Ptem float32

	// Horizontal and vertical scale of the font.
	//
	// The font scale is a number related to, but not the same as,
	// font size. Typically the client establishes a scale factor
	// to be used between the two. For example, 64, or 256, which
	// would be the fractional-precision part of the font scale.
	// This is necessary because [Position] values are integer
	// types and you need to leave room for fractional values
	// in there.
	//
	// For example, to set the font size to 20, with 64
	// levels of fractional precision you would use a scale of
	// 20 * 64.
	//
	// In the example above, even what font size 20 means is up to
	// you. It might be 20 pixels, or 20 points, or 20 millimeters.
	// HarfBuzz does not care about that. You can set the point
	// size of the font using [Ptem], and the pixel
	// size using [Face.Ppem]
	//
	// The choice of scale is yours but needs to be consistent between
	// what you set here, and what you expect out of [Position]
	// as well has draw / paint API output values.
	//
	// Fonts default to a scale equal to the UPEM value of their face.
	// A font with this setting is sometimes called an "unscaled" font.
	//
	// The resulting positions are computed with: fontUnit * Scale / faceUpem,
	// where faceUpem is given by the face.
	//
	// Given a device resolution (in dpi) and a point size, the scale to
	// get result in pixels is given by : pointSize * dpi / 72
	XScale, YScale int32

	// slant sets the "synthetic slant" of a font. By default is zero.
	// Synthetic slant is the graphical skew applied to the font
	// at rendering time.
	//
	// HarfBuzz needs to know this value to adjust shaping results,
	// metrics, and style values to match the slanted rendering.
	//
	// Note: The slant value is a ratio.  For example, a
	// 20% slant would be represented as a 0.2 value.
	slant float32

	// embolden sets the "synthetic boldness" of a font.
	//
	// Positive values for xEmbolden / yEmbolden make a font
	// bolder, negative values thinner. Typical values are in the
	// 0.01 to 0.05 range. The default value is zero.
	//
	// Synthetic boldness is applied by offsetting the contour
	// points of the glyph shape.
	//
	// Glyph advance-widths are also adjusted
	xEmbolden float32
}

// NewFont constructs a new font object from the specified face.
//
// The scale is set to the face Upem, meaning that by default
// the output results will be expressed in font units.
//
// The `face` object should not be modified after this call.
func ( Face) *Font {
	var  Font

	.face = 
	.faceUpem = Position(.face.Upem())
	.XScale = .faceUpem
	.YScale = .faceUpem

	// accelerators
	.gsubAccels = make([]otLayoutLookupAccelerator, len(.GSUB.Lookups))
	for ,  := range .GSUB.Lookups {
		.gsubAccels[].init(lookupGSUB())
	}
	.gposAccels = make([]otLayoutLookupAccelerator, len(.GPOS.Lookups))
	for ,  := range .GPOS.Lookups {
		.gposAccels[].init(lookupGPOS())
	}
	.morxAccels = make([][]morxSubtableAccelerator, len(.Morx))
	for ,  := range .Morx {
		.morxAccels[] = newMorxChainAccelerator()
	}
	.kernAccels = make([]kernxSubtableAccelerator, len(.Kern))
	for ,  := range .Kern {
		.kernAccels[] = newKernxSubtableAccelerator()
	}
	.kerxAccels = make([]kernxSubtableAccelerator, len(.Kerx))
	for ,  := range .Kerx {
		.kerxAccels[] = newKernxSubtableAccelerator()
	}
	return &
}

// SetVarCoordsDesign applies a list of variation coordinates, in design-space units,
// to the font.
func ( *Font) ( []float32) {
	.face.SetCoords(.face.NormalizeVariations())
}

// Face returns the underlying face.
// Note that field is readonly, since some caching may happen
// in the `NewFont` constructor.
func ( *Font) () Face { return .face }

func ( *Font) ( rune,  GID) (GID, bool) {
	,  := .face.NominalGlyph()
	if ! {
		 = 
	}
	return , 
}

func ( *Font) () Position { return roundf(float32(.XScale) * .xEmbolden) }

// ---- Convert from font-space to user-space ----

func ( *Font) ( int16) Position    { return Position() * .XScale / .faceUpem }
func ( *Font) ( int16) Position    { return Position() * .YScale / .faceUpem }
func ( *Font) ( float32) Position { return emScalef(, .XScale, .faceUpem) }
func ( *Font) ( float32) Position { return emScalef(, .YScale, .faceUpem) }
func ( *Font) ( int16) float32    { return emFscale(, .XScale, .faceUpem) }
func ( *Font) ( int16) float32    { return emFscale(, .YScale, .faceUpem) }

func emScalef( float32, ,  int32) Position {
	return roundf( * float32() / float32())
}

func emFscale( int16, ,  int32) float32 {
	return float32() * float32() / float32()
}

// GlyphExtents is the same as fonts.GlyphExtents but with int type
type GlyphExtents struct {
	XBearing int32
	YBearing int32
	Width    int32
	Height   int32
}

// GlyphExtents fetches the GlyphExtents data for a glyph ID
// in the specified font, or false if not found
func ( *Font) ( GID) ( GlyphExtents,  bool) {
	,  := .face.GlyphExtents()
	if ! {
		return , false
	}

	syntheticGlyphExtents(&, .slant, .xEmbolden)

	.XBearing = .emScalefX(.XBearing)
	.Width = .emScalefX(.Width)
	.YBearing = .emScalefY(.YBearing)
	.Height = .emScalefY(.Height)
	return , true
}

// GlyphAdvanceForDirection fetches the advance for a glyph ID from the specified font,
// in a text segment of the specified direction.
//
// Calls the appropriate direction-specific variant (horizontal
// or vertical) depending on the value of `dir`.
func ( *Font) ( GID,  Direction) (,  Position) {
	if .isHorizontal() {
		return .GlyphHAdvance(), 0
	}
	return 0, .getGlyphVAdvance()
}

// GlyphHAdvance fetches the advance for a glyph ID in the font,
// for horizontal text segments.
func ( *Font) ( GID) Position {
	 := .face.HorizontalAdvance()
	 := .emScalefX()
	if .xEmbolden != 0 &&  != 0 {
		 += .xStrength()
	}
	return 
}

// Fetches the advance for a glyph ID in the font,
// for vertical text segments.
func ( *Font) ( GID) Position {
	if .face.HasVerticalMetrics() {
		 := .face.VerticalAdvance()
		return .emScalefY()
	} else {
		 := .fontHExtentsWithFallback()
		 := Position(-(.Ascender - .Descender))
		return 
	}
}

// Subtracts the origin coordinates from an (X,Y) point coordinate,
// in the specified glyph ID in the specified font.
//
// Calls the appropriate direction-specific variant (horizontal
// or vertical) depending on the value of @direction.
func ( *Font) ( GID,  Direction,
	,  Position,
) (Position, Position) {
	,  := .getGlyphOriginForDirection(, )

	return  - ,  - 
}

// Fetches the (X,Y) coordinates of the origin for a glyph in
// the specified font.
//
// Calls the appropriate direction-specific variant (horizontal
// or vertical) depending on the value of @direction.
func ( *Font) ( GID,  Direction) (,  Position) {
	if .isHorizontal() {
		return .getGlyphHOriginWithFallback()
	}
	return .getGlyphVOriginWithFallback()
}

func ( *Font) ( GID) (Position, Position) {
	, ,  := .face.GlyphHOrigin()
	if ! {
		,  := .face.GlyphVOrigin()
		,  := .emScalefX(), .emScalefY()
		,  := .guessVOriginMinusHOrigin()
		return  - ,  - 
	}

	,  := .emScalefX(float32()), .emScalefY(float32())
	if .xEmbolden != 0 {
		/* Slant is ignored as it does not affect glyph origin */

		/* Embolden */
		 := .xStrength()
		 += 
		 += 
	}
	return , 
}

func ( *Font) ( GID) (Position, Position) {
	,  := .face.GlyphVOrigin()
	,  := .emScalefX(2*)/2, .emScalefY() // harfbuzz divides by 2 in Position unit
	/* Slant is ignored as it does not affect glyph origin */
	/* Embolden */
	if .xEmbolden != 0 {
		 := .xStrength()
		 += 
		 += 
	}
	return , 
}

func ( *Font) ( GID) (,  Position) {
	 = .GlyphHAdvance() / 2
	 = .getHExtendsAscender()
	return , 
}

func ( *Font) () Position {
	,  := .face.FontHExtents()
	if ! {
		return .YScale * 4 / 5
	}
	return .emScalefY(.Ascender)
}

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

func ( *Font) ( GID, ,  Position) (Position, Position) {
	,  := .getGlyphHOriginWithFallback()
	return  - ,  - 
}

func ( *Font) ( GID, ,  Position) (Position, Position) {
	,  := .getGlyphVOriginWithFallback()
	return  - ,  - 
}

func ( *Font) ( GID, ,  Position) (Position, Position) {
	,  := .getGlyphHOriginWithFallback()
	return  + ,  + 
}

func ( *Font) ( GID,  uint16,  Direction) (,  Position,  bool) {
	, ,  = .face.GetGlyphContourPoint(, )
	if  {
		,  = .subtractGlyphOriginForDirection(, , , )
	}

	return , , 
}

func ( *Font) () font.FontExtents {
	,  := .face.FontHExtents()
	.Ascender = float32(.emScalefY(.Ascender))
	.Descender = float32(.emScalefY(.Descender))
	.LineGap = float32(.emScalefY(.LineGap))
	if ! {
		.Ascender = float32(.YScale) * 0.8
		.Descender = .Ascender - float32(.YScale)
		.LineGap = 0
	}
	return 
}

// ExtentsForDirection fetches the extents for a font in a text segment of the
// specified direction, applying the scaling.
//
// Calls the appropriate direction-specific variant (horizontal
// or vertical) depending on the value of `direction`.
func ( *Font) ( Direction) font.FontExtents {
	var (
		 font.FontExtents
		      bool
	)
	if .isHorizontal() {
		return .fontHExtentsWithFallback()
	} else {
		,  = .face.FontVExtents()
		.Ascender = float32(.emScalefX(.Ascender))
		.Descender = float32(.emScalefX(.Descender))
		.LineGap = float32(.emScalefX(.LineGap))
		if ! {
			.Ascender = float32(.XScale) * 0.5
			.Descender = .Ascender - float32(.XScale)
			.LineGap = 0
		}

		 := float32(.XScale) * .xEmbolden
		.Ascender += 
	}
	return 
}

func ( *Font) () []tables.Coord { return .face.Coords() }

func ( *Font) ( tables.ItemVarStore,  tables.DeviceTable) Position {
	switch device := .(type) {
	case tables.DeviceHinting:
		,  := .face.Ppem()
		return .GetDelta(, .XScale)
	case tables.DeviceVariation:
		return .emScalefX(.GetDelta(tables.VariationStoreIndex(), .varCoords()))
	default:
		return 0
	}
}

func ( *Font) ( tables.ItemVarStore,  tables.DeviceTable) Position {
	switch device := .(type) {
	case tables.DeviceHinting:
		,  := .face.Ppem()
		return .GetDelta(, .YScale)
	case tables.DeviceVariation:
		return .emScalefY(.GetDelta(tables.VariationStoreIndex(), .varCoords()))
	default:
		return 0
	}
}

func syntheticGlyphExtents( *font.GlyphExtents, ,  float32) {
	/* Slant. */
	if  != 0 {
		 := .XBearing
		 := .YBearing
		 := .XBearing + .Width
		 := .YBearing + .Height

		 += minF(*, *)
		 += maxF(*, *)

		.XBearing = 
		.Width =  - .XBearing
	}

	/* Embolden. */
	if  != 0 {
		/* Y */
		 := 
		.YBearing += 
		.Height -= 

		/* X */
		 := 
		// if embolden_in_place {
		// 	extents.x_bearing -= xShift / 2
		// }
		.Width += 
	}
}

// GetOTLigatureCarets fetches a list of the caret positions defined for a ligature glyph in the GDEF
// table of the font (or nil if not found).
func ( *Font) ( Direction,  GID) []Position {
	 := .face.GDEF.ItemVarStore

	 := .face.GDEF.LigCaretList
	if .Coverage == nil {
		return nil
	}

	,  := .Coverage.Index(gID())
	if ! {
		return nil
	}

	 := .LigGlyphs[].CaretValues
	 := make([]Position, len())
	for ,  := range  {
		[] = .getCaretValue(, , , )
	}
	return 
}

// interpreted the CaretValue according to its format
func ( *Font) ( tables.CaretValue,  Direction,  GID,  tables.ItemVarStore) Position {
	switch caret := .(type) {
	case tables.CaretValue1:
		if .isHorizontal() {
			return .emScaleX(int16(.Coordinate))
		} else {
			return .emScaleY(int16(.Coordinate))
		}
	case tables.CaretValue2:
		, ,  := .getGlyphContourPointForOrigin(, uint16(.CaretValuePointIndex), )
		if .isHorizontal() {
			return 
		} else {
			return 
		}
	case tables.CaretValue3:
		if .isHorizontal() {
			return .emScaleX(.Coordinate) + .getXDelta(, .Device)
		} else {
			return .emScaleY(.Coordinate) + .getYDelta(, .Device)
		}
	default:
		return 0
	}
}