// Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package sfnt

import (
	
)

const (
	hexScriptLatn  = uint32(0x6c61746e) // latn
	hexScriptDFLT  = uint32(0x44464c54) // DFLT
	hexFeatureKern = uint32(0x6b65726e) // kern
)

// kernFunc returns the unscaled kerning value for kerning pair a+b.
// Returns ErrNotFound if no kerning is specified for this pair.
type kernFunc func(a, b GlyphIndex) (int16, error)

func ( *Font) ( []byte) ([]byte, []kernFunc, error) {
	// https://docs.microsoft.com/en-us/typography/opentype/spec/gpos

	if .gpos.length == 0 {
		return , nil, nil
	}
	const  = 10 // GPOS header v1.1 is 14 bytes, but we don't support FeatureVariations
	if .gpos.length <  {
		return , nil, errInvalidGPOSTable
	}

	,  := .src.view(, int(.gpos.offset), )
	if  != nil {
		return , nil, 
	}

	// check for version 1.0/1.1
	if u16() != 1 || u16([2:]) > 1 {
		return , nil, errUnsupportedGPOSTable
	}
	 := u16([4:])
	 := u16([6:])
	 := u16([8:])

	// get all feature indices for latn script
	, ,  := .parseGPOSScriptFeatures(, int(.gpos.offset)+int(), hexScriptLatn)
	if  != nil {
		return , nil, 
	}
	if len() == 0 {
		// get all feature indices for DFLT script
		, ,  = .parseGPOSScriptFeatures(, int(.gpos.offset)+int(), hexScriptDFLT)
		if  != nil {
			return , nil, 
		}
		if len() == 0 {
			return , nil, nil
		}
	}

	// get all lookup indices for kern features
	, ,  := .parseGPOSFeaturesLookup(, int(.gpos.offset)+int(), , hexFeatureKern)
	if  != nil {
		return , nil, 
	}

	// LookupTableList: lookupCount,[]lookups
	, ,  := .src.varLenView(, int(.gpos.offset)+int(), 2, 0, 2)
	if  != nil {
		return , nil, 
	}

	var  []kernFunc

:
	for ,  := range  {
		if  >  {
			return , nil, errInvalidGPOSTable
		}
		 := int(.gpos.offset) + int() + int(u16([2+*2:]))

		// LookupTable: lookupType, lookupFlag, subTableCount, []subtableOffsets, markFilteringSet
		, ,  := .src.varLenView(, , 8, 4, 2)
		if  != nil {
			return , nil, 
		}

		 := u16([2:])

		 := make([]int, )
		for  := 0;  < int(); ++ {
			[] = int() + int(u16([6+*2:]))
		}

		switch  := u16();  {
		case 2: // PairPos table
		case 9:
			// Extension Positioning table defines an additional u32 offset
			// to allow subtables to exceed the 16-bit limit.
			for  := range  {
				,  = .src.view(, [], 8)
				if  != nil {
					return , nil, 
				}
				if  := u16();  != 1 {
					return , nil, errUnsupportedExtensionPosFormat
				}
				if  := u16([2:]);  != 2 {
					continue 
				}
				[] += int(u32([4:]))
			}
		default: // other types are not supported
			continue
		}

		if &0x0010 > 0 {
			// useMarkFilteringSet enabled, skip as it is not supported
			continue
		}

		for ,  := range  {
			,  = .src.view(, int(), 4)
			if  != nil {
				return , nil, 
			}
			 := u16()

			var  indexLookupFunc
			, ,  = .makeCachedCoverageLookup(, +int(u16([2:])))
			if  != nil {
				return , nil, 
			}

			switch  {
			case 1: // Adjustments for Glyph Pairs
				, ,  := .parsePairPosFormat1(, , )
				if  != nil {
					return , nil, 
				}
				if  != nil {
					 = append(, )
				}
			case 2: // Class Pair Adjustment
				, ,  := .parsePairPosFormat2(, , )
				if  != nil {
					return , nil, 
				}
				if  != nil {
					 = append(, )
				}
			}
		}
	}

	return , , nil
}

func ( *Font) ( []byte,  int,  indexLookupFunc) ([]byte, kernFunc, error) {
	// PairPos Format 1: posFormat, coverageOffset, valueFormat1,
	// valueFormat2, pairSetCount, []pairSetOffsets
	var  error
	var  int
	, ,  = .src.varLenView(, , 10, 8, 2)
	if  != nil {
		return , nil, 
	}
	// check valueFormat1 and valueFormat2 flags
	if u16([4:]) != 0x04 || u16([6:]) != 0x00 {
		// we only support kerning with X_ADVANCE for first glyph
		return , nil, nil
	}

	// PairPos table contains an array of offsets to PairSet
	// tables, which contains an array of PairValueRecords.
	// Calculate length of complete PairPos table by jumping to
	// last PairSet.
	// We need to iterate all offsets to find the last pair as
	// offsets are not sorted and can be repeated.
	var  int
	for  := 0;  < ; ++ {
		 := int(u16([10+*2:]))
		if  >  {
			 = 
		}
	}
	,  = .src.view(, +, 2)
	if  != nil {
		return , nil, 
	}

	 := int(u16())
	// Each PairSet contains the secondGlyph (u16) and one or more value records (all u16).
	// We only support lookup tables with one value record (X_ADVANCE, see valueFormat1/2 above).
	 := 2 + *4

	 :=  + 
	,  = .src.view(, , )
	if  != nil {
		return , nil, 
	}

	 := makeCachedPairPosGlyph(, , )
	return , , nil
}

func ( *Font) ( []byte,  int,  indexLookupFunc) ([]byte, kernFunc, error) {
	// PairPos Format 2:
	// posFormat, coverageOffset, valueFormat1, valueFormat2,
	// classDef1Offset, classDef2Offset, class1Count, class2Count,
	// []class1Records
	var  error
	,  = .src.view(, , 16)
	if  != nil {
		return , nil, 
	}
	// check valueFormat1 and valueFormat2 flags
	if u16([4:]) != 0x04 || u16([6:]) != 0x00 {
		// we only support kerning with X_ADVANCE for first glyph
		return , nil, nil
	}
	 := int(u16([12:]))
	 := int(u16([14:]))
	 :=  + int(u16([8:]))
	 :=  + int(u16([10:]))
	var ,  classLookupFunc
	, ,  = .makeCachedClassLookup(, )
	if  != nil {
		return , nil, 
	}
	, ,  = .makeCachedClassLookup(, )
	if  != nil {
		return , nil, 
	}

	,  = .src.view(, +16, **2)
	if  != nil {
		return , nil, 
	}
	 := makeCachedPairPosClass(
		,
		,
		,
		,
		,
		,
	)

	return , , nil
}

// parseGPOSScriptFeatures returns all indices of features in FeatureTable that
// are valid for the given script.
// Returns features from DefaultLangSys, different languages are not supported.
// However, all observed fonts either do not use different languages or use the
// same features as DefaultLangSys.
func ( *Font) ( []byte,  int,  uint32) ([]byte, []int, error) {
	// ScriptList table: scriptCount, []scriptRecords{scriptTag, scriptOffset}
	, ,  := .src.varLenView(, , 2, 0, 6)
	if  != nil {
		return , nil, 
	}

	// Search ScriptTables for script
	var  uint16
	for  := 0;  < ; ++ {
		 := u32([2+*6:])
		if  ==  {
			 = u16([2+*6+4:])
			break
		}
	}
	if  == 0 {
		return , nil, nil
	}

	// Script table: defaultLangSys, langSysCount, []langSysRecords{langSysTag, langSysOffset}
	,  = .src.view(, +int(), 2)
	if  != nil {
		return , nil, 
	}
	 := u16()

	if  == 0 {
		return , nil, nil
	}

	// LangSys table: lookupOrder (reserved), requiredFeatureIndex, featureIndexCount, []featureIndices
	, ,  := .src.varLenView(, +int()+int(), 6, 4, 2)
	if  != nil {
		return , nil, 
	}

	 := make([]int, )
	for  := range  {
		[] = int(u16([6+*2:]))
	}
	return , , nil
}

func ( *Font) ( []byte,  int,  []int,  uint32) ([]byte, []int, error) {
	// FeatureList table: featureCount, []featureRecords{featureTag, featureOffset}
	, ,  := .src.varLenView(, , 2, 0, 6)
	if  != nil {
		return , nil, 
	}

	 := make([]int, 0, 4)

	for ,  := range  {
		if  >  {
			return , nil, errInvalidGPOSTable
		}
		 := u32([2+*6:])
		if  !=  {
			continue
		}
		 := u16([2+*6+4:])

		, ,  := .src.varLenView(nil, +int(), 4, 2, 2)
		if  != nil {
			return , nil, 
		}

		for  := 0;  < ; ++ {
			 = append(, int(u16([4+*2:])))
		}
	}

	return , , nil
}

func makeCachedPairPosGlyph( indexLookupFunc,  int,  []byte) kernFunc {
	 := make([]byte, len())
	copy(, )
	return func(,  GlyphIndex) (int16, error) {
		,  := ()
		if ! {
			return 0, ErrNotFound
		}
		if  >=  {
			return 0, ErrNotFound
		}
		 := int(u16([10+*2:]))
		if +1 >= len() {
			return 0, errInvalidGPOSTable
		}

		 := int(u16([:]))
		for  := 0;  < ; ++ {
			 := GlyphIndex(int(u16([+2+*4:])))
			if  ==  {
				return int16(u16([+2+*4+2:])), nil
			}
			if  >  {
				return 0, ErrNotFound
			}
		}

		return 0, ErrNotFound
	}
}

func makeCachedPairPosClass( indexLookupFunc, ,  int, ,  classLookupFunc,  []byte) kernFunc {
	 := make([]byte, len())
	copy(, )
	return func(,  GlyphIndex) (int16, error) {
		// check coverage to avoid selection of default class 0
		,  := ()
		if ! {
			return 0, ErrNotFound
		}
		 := ()
		 := ()
		return int16(u16([(+*)*2:])), nil
	}
}

// indexLookupFunc returns the index into a PairPos table for the provided glyph.
// Returns false if the glyph is not covered by this lookup.
type indexLookupFunc func(GlyphIndex) (int, bool)

func ( *Font) ( []byte,  int) ([]byte, indexLookupFunc, error) {
	var  error
	,  = .src.view(, , 2)
	if  != nil {
		return , nil, 
	}
	switch u16() {
	case 1:
		// Coverage Format 1: coverageFormat, glyphCount, []glyphArray
		, _,  = .src.varLenView(, , 4, 2, 2)
		if  != nil {
			return , nil, 
		}
		return , makeCachedCoverageList([2:]), nil
	case 2:
		// Coverage Format 2: coverageFormat, rangeCount, []rangeRecords{startGlyphID, endGlyphID, startCoverageIndex}
		, _,  = .src.varLenView(, , 4, 2, 6)
		if  != nil {
			return , nil, 
		}
		return , makeCachedCoverageRange([2:]), nil
	default:
		return , nil, errUnsupportedCoverageFormat
	}
}

func makeCachedCoverageList( []byte) indexLookupFunc {
	 := int(u16())
	 := make([]byte, len()-2)
	copy(, [2:])
	return func( GlyphIndex) (int, bool) {
		 := sort.Search(, func( int) bool {
			return  <= GlyphIndex(u16([*2:]))
		})
		if  <  && GlyphIndex(u16([*2:])) ==  {
			return , true
		}

		return 0, false
	}
}

func makeCachedCoverageRange( []byte) indexLookupFunc {
	 := int(u16())
	 := make([]byte, len()-2)
	copy(, [2:])
	return func( GlyphIndex) (int, bool) {
		if  == 0 {
			return 0, false
		}

		// ranges is an array of startGlyphID, endGlyphID and startCoverageIndex
		// Ranges are non-overlapping.
		// The following GlyphIDs/index pairs are stored as follows:
		//	 pairs: 130=0, 131=1, 132=2, 133=3, 134=4, 135=5, 137=6
		//   ranges: 130, 135, 0    137, 137, 6
		// startCoverageIndex is used to calculate the index without counting
		// the length of the preceding ranges

		 := sort.Search(, func( int) bool {
			return  <= GlyphIndex(u16([*6:]))
		})
		// idx either points to a matching start, or to the next range (or idx==num)
		// e.g. with the range example from above: 130 points to 130-135 range, 133 points to 137-137 range

		// check if gi is the start of a range, but only if sort.Search returned a valid result
		if  <  {
			if  := u16([*6:]);  == GlyphIndex() {
				return int(u16([*6+4:])), true
			}
		}
		// check if gi is in previous range
		if  > 0 {
			--
			,  := u16([*6:]), u16([*6+2:])
			if  >= GlyphIndex() &&  <= GlyphIndex() {
				return int(u16([*6+4:]) + uint16() - ), true
			}
		}

		return 0, false
	}
}

// classLookupFunc returns the class ID for the provided glyph. Returns 0
// (default class) for glyphs not covered by this lookup.
type classLookupFunc func(GlyphIndex) int

func ( *Font) ( []byte,  int) ([]byte, classLookupFunc, error) {
	var  error
	,  = .src.view(, , 2)
	if  != nil {
		return , nil, 
	}
	switch u16() {
	case 1:
		// ClassDefFormat 1: classFormat, startGlyphID, glyphCount, []classValueArray
		, _,  = .src.varLenView(, , 6, 4, 2)
		if  != nil {
			return , nil, 
		}
		return , makeCachedClassLookupFormat1(), nil
	case 2:
		// ClassDefFormat 2: classFormat, classRangeCount, []classRangeRecords
		, _,  = .src.varLenView(, , 4, 2, 6)
		if  != nil {
			return , nil, 
		}
		return , makeCachedClassLookupFormat2(), nil
	default:
		return , nil, errUnsupportedClassDefFormat
	}
}

func makeCachedClassLookupFormat1( []byte) classLookupFunc {
	 := u16([2:])
	 := u16([4:])
	 := make([]byte, len()-4)
	copy(, [6:])

	return func( GlyphIndex) int {
		// classIDs is an array of target class IDs. gi is the index into that array (minus startGI).
		if  < GlyphIndex() ||  >= GlyphIndex(+) {
			// default to class 0
			return 0
		}
		return int(u16([(int()-int())*2:]))
	}
}

func makeCachedClassLookupFormat2( []byte) classLookupFunc {
	 := int(u16([2:]))
	 := make([]byte, len()-2)
	copy(, [4:])

	return func( GlyphIndex) int {
		if  == 0 {
			return 0 // default to class 0
		}

		// classRange is an array of startGlyphID, endGlyphID and target class ID.
		// Ranges are non-overlapping.
		// E.g. 130, 135, 1   137, 137, 5   etc

		 := sort.Search(, func( int) bool {
			return  <= GlyphIndex(u16([*6:]))
		})
		// idx either points to a matching start, or to the next range (or idx==num)
		// e.g. with the range example from above: 130 points to 130-135 range, 133 points to 137-137 range

		// check if gi is the start of a range, but only if sort.Search returned a valid result
		if  <  {
			if  := u16([*6:]);  == GlyphIndex() {
				return int(u16([*6+4:]))
			}
		}
		// check if gi is in previous range
		if  > 0 {
			--
			,  := u16([*6:]), u16([*6+2:])
			if  >= GlyphIndex() &&  <= GlyphIndex() {
				return int(u16([*6+4:]))
			}
		}
		// default to class 0
		return 0
	}
}