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

package font

import (
	
	
	
	
	
	

	ot 
	
	
)

type contourPoint struct {
	SegmentPoint

	isOnCurve  bool
	isEndPoint bool // this point is the last of the current contour
	isExplicit bool // this point is referenced, i.e., explicit deltas specified */
}

func ( *contourPoint) (,  float32) {
	.X += 
	.Y += 
}

func ( *contourPoint) ( [4]float32) {
	 := .X*[0] + .Y*[2]
	.Y = .X*[1] + .Y*[3]
	.X = 
}

const (
	phantomLeft = iota
	phantomRight
	phantomTop
	phantomBottom
	phantomCount
)

type glyphSet map[tables.GlyphID]struct{}

func ( *Face) ( tables.GlyphID) []contourPoint {
	var  []contourPoint
	 := make(glyphSet) // used to deny loops
	.getPointsForGlyphRec(, 0, , &)
	return 
}

const maxCompositeNesting = 20 // protect against malicious fonts

// use the `glyf` table to fetch the contour points,
// applying variation if needed.
// for composite, recursively calls itself; allPoints includes phantom points and will be at least of length 4
func ( *Face) ( tables.GlyphID,  int,  glyphSet,  *[]contourPoint /* OUT */) {
	// adapted from harfbuzz/src/OT/glyf/Glyph.hh

	if  > maxCompositeNesting || int() >= len(.glyf) {
		return
	}

	 := .glyf[]

	var  []contourPoint
	if ,  := .Data.(tables.SimpleGlyph);  {
		 = getContourPoints() // fetch the "real" points
	} else { // zeros values are enough
		 = make([]contourPoint, pointNumbersCount())
	}

	// init phantom point
	 = append(, make([]contourPoint, phantomCount)...)
	 := [len()-phantomCount:]

	 := float32(.XMin - .hmtx.SideBearing())
	 := float32(.YMax + .vmtx.SideBearing())
	 := float32(.getBaseAdvance(, .hmtx, false))
	 := float32(.getBaseAdvance(, .vmtx, true))
	[phantomLeft].X = 
	[phantomRight].X =  + 
	[phantomTop].Y = 
	[phantomBottom].Y =  - 

	if .isVar() {
		.gvar.applyDeltasToPoints(, .coords, )
	}

	switch data := .Data.(type) {
	case tables.SimpleGlyph:
		* = append(*, ...)
	case tables.CompositeGlyph:
		for ,  := range .Glyphs {
			if ,  := [.GlyphIndex];  {
				continue
			}
			[.GlyphIndex] = struct{}{}

			// recurse on component
			var  []contourPoint

			.(.GlyphIndex, +1, , &)

			 := len()
			if  < phantomCount { // in case of max depth reached
				delete(, .GlyphIndex)
				return
			}

			/* Copy phantom points from component if USE_MY_METRICS flag set */
			if .HasUseMyMetrics() {
				copy(, [-phantomCount:])
			}

			/* Apply component transformation & translation */
			transformPoints(&, )

			/* Apply translation from gvar */
			,  := [].X, [].Y
			for  := range  {
				[].translate(, )
			}

			if .IsAnchored() {
				,  := .ArgsAsIndices()
				if  < len(*) &&  <  {
					,  := (*)[].X-[].X, (*)[].Y-[].Y
					for  := range  {
						[].translate(, )
					}
				}
			}

			* = append(*, [0:-phantomCount]...)

			delete(, .GlyphIndex)
		}

		* = append(*, ...)
	default: // no data for the glyph
		* = append(*, ...)
	}

	// apply at top level
	if  == 0 {
		/* Undocumented rasterizer behavior:
		 * Shift points horizontally by the updated left side bearing */
		 := -[phantomLeft].X
		for  := range * {
			(*)[].translate(, 0)
		}
	}
}

// does not includes phantom points
func pointNumbersCount( tables.Glyph) int {
	switch g := .Data.(type) {
	case tables.SimpleGlyph:
		return len(.Points)
	case tables.CompositeGlyph:
		/* pseudo component points for each component in composite glyph */
		return len(.Glyphs)
	}
	return 0
}

// return all the contour points, without phantoms
func getContourPoints( tables.SimpleGlyph) []contourPoint {
	const  = 1 << 0 // 0x0001

	 := make([]contourPoint, len(.Points))
	for ,  := range .EndPtsOfContours {
		[].isEndPoint = true
	}
	for ,  := range .Points {
		[].X, [].Y = float32(.X), float32(.Y)
		[].isOnCurve = .Flag& != 0
	}
	return 
}

func extentsFromPoints( []contourPoint) ( GlyphExtents) {
	 := [:len()-phantomCount]
	if len() == 0 {
		// zero extent for the empty glyph
		return 
	}
	,  := [0].X, [0].Y
	,  := , 
	for ,  := range  {
		 = minF(, .X)
		 = minF(, .Y)
		 = maxF(, .X)
		 = maxF(, .Y)
	}
	.XBearing = 
	.YBearing = 
	.Width =  - 
	.Height =  - 
	return 
}

// walk through the contour points of the given glyph to compute its extends and its phantom points
// As an optimization, if `computeExtents` is false, the extents computation is skipped (a zero value is returned).
func ( *Face) ( tables.GlyphID,  bool) ( GlyphExtents,  [phantomCount]contourPoint) {
	if int() >= len(.glyf) {
		return
	}
	 := .getPointsForGlyph()

	copy([:], [len()-phantomCount:])

	if  {
		 = extentsFromPoints()
	}

	return , 
}

func min16(,  int16) int16 {
	if  <  {
		return 
	}
	return 
}

func max16(,  int16) int16 {
	if  >  {
		return 
	}
	return 
}

func minC(,  VarCoord) VarCoord {
	if  <  {
		return 
	}
	return 
}

func maxC(,  VarCoord) VarCoord {
	if  >  {
		return 
	}
	return 
}

func minF(,  float32) float32 {
	if  <  {
		return 
	}
	return 
}

func maxF(,  float32) float32 {
	if  >  {
		return 
	}
	return 
}

func transformPoints( *tables.CompositeGlyphPart,  []contourPoint) {
	var ,  float32
	if !.IsAnchored() {
		,  := .ArgsAsTranslation()
		,  = float32(), float32()
	}

	 := .Scale
	// shortcut identity transform
	if  == 0 &&  == 0 &&  == [4]float32{1, 0, 0, 1} {
		return
	}

	if .IsScaledOffsets() {
		for  := range  {
			[].translate(, )
			[].transform()
		}
	} else {
		for  := range  {
			[].transform()
			[].translate(, )
		}
	}
}

func getGlyphExtents( tables.Glyph,  tables.Hmtx,  gID) GlyphExtents {
	var  GlyphExtents
	/* Undocumented rasterizer behavior: shift glyph to the left by (lsb - xMin), i.e., xMin = lsb */
	/* extents.XBearing = hb_min (glyph_header.xMin, glyph_header.xMax); */
	.XBearing = float32(.SideBearing())

	.YBearing = float32(max16(.YMin, .YMax))
	.Width = float32(max16(.XMin, .XMax) - min16(.XMin, .XMax))
	.Height = float32(min16(.YMin, .YMax) - max16(.YMin, .YMax))
	return 
}

// sbix

var (
	dupe = ot.MustNewTag("dupe")
	// tagPNG identifies bitmap glyph with png format
	tagPNG = ot.MustNewTag("png ")
	// tagTIFF identifies bitmap glyph with tiff format
	tagTIFF = ot.MustNewTag("tiff")
	// tagJPG identifies bitmap glyph with jpg format
	tagJPG = ot.MustNewTag("jpg ")
)

// strikeGlyph return the data for [glyph], or a zero value if not found.
func strikeGlyph( *tables.Strike,  gID,  int) tables.BitmapGlyphData {
	const  = 8

	if int() >= len(.GlyphDatas) {
		return tables.BitmapGlyphData{}
	}
	 := .GlyphDatas[]
	if .GraphicType == dupe {
		if len(.Data) < 2 ||  >  {
			return tables.BitmapGlyphData{}
		}
		 = gID(binary.BigEndian.Uint16(.Data))
		return (, , +1)
	}
	return 
}

// decodeBitmapConfig parse the data to find the width and height
func decodeBitmapConfig( tables.BitmapGlyphData) (,  int,  BitmapFormat,  error) {
	var  image.Config
	switch .GraphicType {
	case tagPNG:
		 = PNG
		,  = png.DecodeConfig(bytes.NewReader(.Data))
	case tagTIFF:
		 = TIFF
		,  = tiff.DecodeConfig(bytes.NewReader(.Data))
	case tagJPG:
		 = JPG
		,  = jpeg.DecodeConfig(bytes.NewReader(.Data))
	default:
		 = fmt.Errorf("unsupported graphic type in sbix table: %s", .GraphicType)
	}
	if  != nil {
		return 0, 0, 0, 
	}
	return .Width, .Height, , nil
}

// return the extents computed from the data
// should only be called on valid, non nil glyph data
func bitmapGlyphExtents( tables.BitmapGlyphData) ( GlyphExtents,  bool) {
	, , ,  := decodeBitmapConfig()
	if  != nil {
		return , false
	}
	.XBearing = float32(.OriginOffsetX)
	.YBearing = float32() + float32(.OriginOffsetY)
	.Width = float32()
	.Height = -float32()
	return , true
}