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

package font

import (
	
	
	
	

	
)

// axis records
type fvar []tables.VariationAxisRecord

func newFvar( tables.Fvar) fvar { return .FvarRecords.Axis }

type mvar struct {
	store  tables.ItemVarStore
	values []tables.VarValueRecord
}

func newMvar( tables.MVAR,  int) (mvar, error) {
	if  := .ItemVariationStore.AxisCount();  !=  {
		return mvar{}, fmt.Errorf("mvar: invalid number of axis (%d != %d)", , )
	}
	return mvar{.ItemVariationStore, .ValueRecords}, nil
}

// return 0 if `tag` is not found
func ( mvar) ( Tag,  []VarCoord) float32 {
	// binary search
	for ,  := 0, len(.values);  < ; {
		 :=  + (-)/2
		 := .values[]
		if  < .ValueTag {
			 = 
		} else if .ValueTag <  {
			 =  + 1
		} else {
			return .store.GetDelta(.Index, )
		}
	}
	return 0
}

// ---------------------------------- gvar ----------------------------------

type gvar struct {
	sharedTuples         [][]VarCoord       // with size tupleCount x axisCount
	variations           [][]tupleVariation // with length glyphCount
	sharedTupleActiveIdx []int              // with length tupleCount
}

func newGvar( tables.Gvar,  tables.Glyf) (gvar, error) {
	if len(.GlyphVariationDatas) != len() {
		return gvar{}, fmt.Errorf("invalid 'gvar' table: mismatch in glyphs count")
	}

	 := gvar{
		sharedTuples:         make([][]VarCoord, len(.SharedTuples.SharedTuples)),
		variations:           make([][]tupleVariation, len(.GlyphVariationDatas)),
		sharedTupleActiveIdx: make([]int, len(.SharedTuples.SharedTuples)),
	}
	for ,  := range .SharedTuples.SharedTuples {
		.sharedTuples[] = .Values
	}
	for ,  := range .GlyphVariationDatas {
		 := make([]tupleVariation, len(.TupleVariationHeaders))
		for ,  := range .TupleVariationHeaders {
			[].TupleVariationHeader = 
		}

		 := pointNumbersCount([]) + phantomCount
		 := parseGlyphVariationSerializedData(.SerializedData,
			.HasSharedPointNumbers(), , false, )
		if  != nil {
			return , 
		}
		.variations[] = 
	}

	// For shared tuples that only have one axis active, share the index of
	// that axis as a cache. This will speed up caclulateScalar() a lot
	// for fonts with lots of axes and many "monovar" tuples.
	for ,  := range .sharedTuples {
		 := -1
		for ,  := range  {
			if  != 0 {
				if  != -1 { // two peaks or more, do not cache
					 = -1
					break
				}
				 = 
			}
		}
		.sharedTupleActiveIdx[] = 
	}

	return , nil
}

type tupleVariation struct {
	tables.TupleVariationHeader

	pointNumbers []uint16 // nil means allPointsNumbers
	// length 2*len(pointNumbers) for gvar table or 2*allPointsNumbers if zero
	deltas []int16
}

// sharedTuples has length tupleCount x axisCount
// sharedTupleActiveIdx has length tupleCount
func ( tupleVariation) ( []VarCoord,  [][]VarCoord,  []int) float32 {
	,  := 0, len()
	 := .PeakTuple.Values
	if  == nil { // no peak specified -> use shared tuple
		 := .Index()
		if int() >= len() { // should not happend
			return 0.
		}
		 = []

		// use the cache to restrict the range
		if  := [];  != -1 {
			 = 
			 =  + 1
		}
	}

	,  := .IntermediateTuples[0].Values, .IntermediateTuples[1].Values
	 :=  != nil

	var  float32 = 1.
	for  := ;  < ; ++ {
		,  := [], []
		if  == 0 ||  ==  {
			continue
		}

		if  {
			 := []
			 := []
			if  >  ||  >  || ( < 0 &&  > 0 &&  != 0) {
				continue
			}
			if  <  ||  >  {
				return 0.
			}
			if  <  {
				if  !=  {
					 *= float32(-) / float32(-)
				}
			} else {
				if  !=  {
					 *= float32(-) / float32(-)
				}
			}
		} else if  == 0 ||  < minC(0, ) ||  > maxC(0, ) {
			return 0.
		} else {
			 *= float32() / float32()
		}
	}
	return 
}

// complete `out`, which contains the parsed tuple headers.
// pointNumbersCountAll is used when the tuple variation data provides deltas for all glyph points
func parseGlyphVariationSerializedData( []byte,  bool,  int,  bool,  []tupleVariation) error {
	var (
		 []uint16
		                error
	)
	if  {
		, ,  = parsePointNumbers()
		if  != nil {
			return 
		}
	}

	for ,  := range  {
		// adjust for the next iteration
		if len() < int(.VariationDataSize) {
			return errors.New("invalid glyph variation serialized data (EOF)")
		}
		 := [.VariationDataSize:]

		// default to shared points
		 := 
		if .HasPrivatePointNumbers() {
			, ,  = parsePointNumbers()
			if  != nil {
				return 
			}
		}
		// the number of point is precised or defaut to all the points
		 := 
		if  != nil {
			 = len()
		}

		[].pointNumbers = 

		if ! {
			 *= 2 // for X and Y
		}

		[].deltas,  = unpackDeltas(, )
		if  != nil {
			return 
		}

		 = 
	}
	return nil
}

// the returned slice is nil if all glyph points are used
func parsePointNumbers( []byte) ([]uint16, []byte, error) {
	, ,  := getPackedPointCount()
	if  != nil {
		return nil, nil, 
	}
	if  == 0 {
		return nil, , nil
	}

	var  uint16
	 := make([]uint16, 0, ) // max value of count is 32767
	for len() < int() {     // loop through the runs
		if len() == 0 {
			return nil, nil, errors.New("invalid glyph variation points numbers (EOF)")
		}
		 := [0]
		 := &0x80 != 0
		 := int(&0x7F + 1)
		if  {
			,  := tables.ParseUint16s([1:], )
			if  != nil {
				return nil, nil, fmt.Errorf("invalid glyph variation points numbers: %s", )
			}
			for ,  := range  {
				 :=  + 
				 = append(, )
				 = 
			}
			 = [1+2*:]
		} else {
			if len() < 1+ {
				return nil, nil, errors.New("invalid glyph variation points numbers (EOF)")
			}
			for ,  := range [1 : 1+] {
				 := uint16() + 
				 = append(, )
				 = 
			}
			 = [1+:]
		}
	}

	return , , nil
}

// return the remaining data and special case of 00
func getPackedPointCount( []byte) (uint16, []byte, error) {
	const  byte = 1 << 7
	if len() < 1 {
		return 0, nil, errors.New("invalid glyph variation points numbers (EOF)")
	}
	if [0] == 0 {
		return 0, [1:], nil
	} else if [0]& == 0 {
		 := uint16([0])
		return , [1:], nil
	} else {
		if len() < 2 {
			return 0, nil, errors.New("invalid glyph variation points numbers (EOF)")
		}
		 := uint16([0]&^)<<8 | uint16([1])
		return , [2:], nil
	}
}

func unpackDeltas( []byte,  int) ([]int16, error) {
	const (
		     = 0x80
		    = 0x40
		 = 0x3F
	)
	 := make([]int16, )
	 := 0 // number of point read : out[:nbRead] is valid
	// The data is read until the expected logic count of deltas is obtained.
	for  <  {
		if len() == 0 {
			return nil, errors.New("invalid packed deltas (EOF)")
		}
		 := [0]
		 := & + 1
		if  := & != 0;  {
			//  no additional value to read, just fill with zeros
			 += int()
			 = [1:]
		} else {
			// we want to fill out[nbRead:nbRead+count-1], that is we must have
			// nbRead+count-1 < pointNumbersCount, ie
			// nbRead+count <= pointNumbersCount
			if  :=  + int();  >  {
				return nil, fmt.Errorf("invalid packed deltas (expected %d point numbers, got %d)", , )
			}
			 := & != 0
			if  {
				if len() < 1+2*int() {
					return nil, errors.New("invalid packed deltas (EOF)")
				}
				for  := byte(0);  < ; ++ { // count < 64 -> no overflow
					[] = int16(binary.BigEndian.Uint16([1+2*:]))
					++
				}
				 = [1+2*:]
			} else {
				if len() < 1+int() {
					return nil, errors.New("invalid packed deltas (EOF)")
				}
				for  := byte(0);  < ; ++ { // count < 64 -> no overflow
					[] = int16(int8([1+]))
					++
				}
				 = [1+:]
			}
		}
	}
	return , nil
}

// update `points` in place
func ( gvar) ( gID,  []VarCoord,  []contourPoint) {
	// adapted from harfbuzz/src/hb-ot-var-gvar-table.hh
	const  = false

	if int() >= len(.variations) { // should not happend
		return
	}

	// save original points for inferred delta calculation
	 := append([]contourPoint(nil), ...)
	// flag is used to indicate referenced point
	 := make([]contourPoint, len())

	 := .variations[]
	for ,  := range  {
		 := .calculateScalar(, .sharedTuples, .sharedTupleActiveIdx)
		if  == 0 {
			continue
		}
		 := len(.deltas)
		 := .pointNumbers == nil
		,  := .deltas[:/2], .deltas[/2:]

		// reset the current deltas
		for  := range  {
			[] = contourPoint{}
		}

		for  := range  {
			 := uint16()
			if ! {
				 = .pointNumbers[]
			}
			[].isExplicit = true
			[].X += float32([]) * 
			[].Y += float32([]) * 
		}

		/* infer deltas for unreferenced points */
		if ! && ! {
			,  := 0, 0
			for {
				for  < len() && ![].isEndPoint {
					++
				}
				if  == len() {
					break
				}

				// check the number of unreferenced points in a contour.
				// If no unref points or no ref points, nothing to do.
				 := 0
				for ,  := range [ : +1] {
					if !.isExplicit {
						++
					}
				}
				 := 
				if  == 0 ||  > - {
					goto 
				}

				for {
					/* Locate the next gap of unreferenced points between two referenced points prev and next.
					 * Note that a gap may wrap around at left (startPoint) and/or at right (endPoint).
					 */
					var , ,  int
					for {
						 = 
						 = nextIndex(, , )
						if [].isExplicit && ![].isExplicit {
							break
						}
					}
					,  = , 
					for {
						 = 
						 = nextIndex(, , )
						if ![].isExplicit && [].isExplicit {
							break
						}
					}
					 = 
					/* Infer deltas for all unref points in the gap between prev and next */
					 = 
					for {
						 = nextIndex(, , )
						if  ==  {
							break
						}
						[].X = inferDelta([].X, [].X, [].X, [].X, [].X)
						[].Y = inferDelta([].Y, [].Y, [].Y, [].Y, [].Y)
						--
						if  == 0 {
							goto 
						}
					}
				}
			:
				 =  + 1
				 = 
			}
		}

		// apply specified / inferred deltas to points
		for ,  := range  {
			[].translate(.X, .Y)
		}
	}
}

func nextIndex(, ,  int) int {
	if  >=  {
		return 
	}
	return  + 1
}

func inferDelta(, , , ,  float32) float32 {
	if  ==  {
		if  ==  {
			return 
		}
		return 0
	} else if  <= minF(, ) {
		if  <  {
			return 
		}
		return 
	} else if  >= maxF(, ) {
		if  >  {
			return 
		}
		return 
	}

	// linear interpolation
	 := ( - ) / ( - )
	return  + *(-)
}

func sanitizeGDEF( tables.GDEF,  int) error {
	// check axis count
	if  := .ItemVarStore.AxisCount();  != -1 &&  !=  {
		return fmt.Errorf("GDEF: invalid number of axis (%d != %d)", , )
	}

	// check LigCarets length
	if .LigCaretList.Coverage != nil {
		 := .LigCaretList.Coverage.Len()
		 := len(.LigCaretList.LigGlyphs)
		if  !=  {
			return fmt.Errorf("GDEF: invalid number of lig gyphs (%d != %d)", , )
		}
	}
	return nil
}

// ------------------------------------- external API -------------------------------------

// Variation defines a value for a wanted variation axis.
type Variation struct {
	Tag   Tag     // Variation-axis identifier tag
	Value float32 // In design units
}

// SetVariations applies a list of font-variation settings to a font,
// defaulting to the values given in the `fvar` table.
// Note that passing an empty slice will instead remove the coordinates.
func ( *Face) ( []Variation) {
	if len() == 0 {
		.SetCoords(nil)
		return
	}

	 := .Font.fvar
	if len() == 0 { // the font is not variable...
		.SetCoords(nil)
		return
	}

	 := .getDesignCoordsDefault()

	.SetCoords(.NormalizeVariations())
}

// getDesignCoordsDefault returns the design coordinates corresponding to the given pairs of axis/value.
// The default value of the axis is used when not specified in the variations.
func ( fvar) ( []Variation) []float32 {
	 := make([]float32, len())
	// start with default values
	for ,  := range  {
		[] = .Default
	}

	.getDesignCoords(, )

	return 
}

// getDesignCoords updates the design coordinates, with the given pairs of axis/value.
// It will panic if `designCoords` has not the length expected by the table, that is the number of axis.
func ( fvar) ( []Variation,  []float32) {
	for ,  := range  {
		// allow for multiple axis with the same tag
		for ,  := range  {
			if .Tag == .Tag {
				[] = .Value
			}
		}
	}
}

// normalize based on the [min,def,max] values for the axis to be [-1,0,1].
func ( fvar) ( []float32) []VarCoord {
	 := make([]VarCoord, len())
	for ,  := range  {
		 := []

		// out of range: clamping
		if  > .Maximum {
			 = .Maximum
		} else if  < .Minimum {
			 = .Minimum
		}

		if  < .Default {
			 = -( - .Default) / (.Minimum - .Default)
		} else if  > .Default {
			 = ( - .Default) / (.Maximum - .Default)
		} else {
			 = 0
		}

		[] = VarCoord(math.Round(float64( * 16384))) // 1 << 14
	}
	return 
}

// NormalizeVariations normalize the given design-space coordinates. The minimum and maximum
// values for the axis are mapped to the interval [-1,1], with the default
// axis value mapped to 0.
//
// Any additional scaling defined in the face's `avar` table is also
// applied, as described at https://docs.microsoft.com/en-us/typography/opentype/spec/avar.
//
// This method panics if `coords` has not the correct length, that is the number of axis inf 'fvar'.
func ( *Font) ( []float32) []VarCoord {
	// Axis normalization is a two-stage process.  First we normalize
	// based on the [min,def,max] values for the axis to be [-1,0,1].
	// Then, if there's an `avar' table, we renormalize this range.
	 := .fvar.normalizeCoordinates()

	// now applying 'avar'
	for ,  := range .avar.AxisSegmentMaps {
		[] = .Map([])
	}

	return 
}