package cff

import (
	
	
	

	ps 
	
)

//go:generate ../../../../typesetting-utils/generators/binarygen/cmd/generator . _src.go

// CFF2 represents a parsed 'CFF2' Opentype table.
type CFF2 struct {
	fdSelect fdSelect // maybe nil if there is only one font dict

	// Charstrings contains the actual glyph definition.
	// It has a length of numGlyphs and is indexed by glyph ID.
	// See `LoadGlyph` for a way to intepret the glyph data.
	Charstrings [][]byte

	globalSubrs [][]byte

	// array of length 1 if fdSelect is nil
	// otherwise, it can be safely indexed by `fdSelect` output
	fonts []privateFonts

	VarStore tables.ItemVarStore // optional
}

type privateFonts struct {
	localSubrs     [][]byte
	defaultVSIndex int32
}

// ParseCFF2 parses 'src', which must be the content of a 'CFF2' Opentype table.
//
// See also https://learn.microsoft.com/en-us/typography/opentype/spec/cff2
func ( []byte) (*CFF2, error) {
	if  := len();  < 5 {
		return nil, fmt.Errorf("reading header: EOF: expected length: 5, got %d", )
	}
	var  header2
	.mustParse()
	 := int(.headerSize) + int(.topDictLength)
	if  := len();  <  {
		return nil, fmt.Errorf("reading topDict: EOF: expected length: %d, got %d", , )
	}
	 := [.headerSize:]

	var (
		  topDict2
		 ps.Machine
	)
	if  := .Run(, nil, nil, &);  != nil {
		return nil, fmt.Errorf("reading top dict: %s", )
	}

	var (
		 CFF2
		 error
	)

	.globalSubrs,  = parseIndex2(, )
	if  != nil {
		return nil, 
	}

	// parse charstrings
	.Charstrings,  = parseIndex2(, int(.charStrings))
	if  != nil {
		return nil, 
	}

	,  := parseIndex2(, int(.fdArray))
	if  != nil {
		return nil, 
	}

	.fonts = make([]privateFonts, len())
	// private dict reference
	for ,  := range  {
		var  fontDict2
		 = .Run(, nil, nil, &)
		if  != nil {
			return nil, fmt.Errorf("reading font dict: %s", )
		}
		 := int(.privateDictOffset + .privateDictSize)
		if  := len();  <  {
			return nil, fmt.Errorf("reading private dict: EOF: expected length: %d, got %d", , )
		}
		// parse private dict
		var  privateDict2
		 = .Run([.privateDictOffset:], nil, nil, &)
		if  != nil {
			return nil, fmt.Errorf("reading private dict: %s", )
		}

		.fonts[].defaultVSIndex = .vsindex
		// if required, parse the local subroutines
		if .subrsOffset != 0 {
			.fonts[].localSubrs,  = parseIndex2(, int(.subrsOffset))
			if  != nil {
				return nil, 
			}
		}
	}

	if len() > 1 {
		// parse the fdSelect
		if  := len();  < int(.fdSelect) {
			return nil, fmt.Errorf("reading fdSelect: EOF: expected length: %d, got %d", .fdSelect, )
		}
		.fdSelect, _,  = parseFdSelect([.fdSelect:], len(.Charstrings))
		if  != nil {
			return nil, 
		}

		// sanitize fdSelect outputs
		 := .fdSelect.extent()
		if len() <  {
			return nil, fmt.Errorf("invalid number of font dicts: %d (for %d)", len(), )
		}
	}

	// parse variation store
	if .vstore != 0 {
		// See https://learn.microsoft.com/en-us/typography/opentype/spec/cff2#variationstore-data-contents
		if ,  := int(.vstore)+2, len();  <  {
			return nil, fmt.Errorf("reading variation store: EOF: expected length: %d, got %d", , )
		}
		 := int(binary.BigEndian.Uint16([.vstore:]))
		 := int(.vstore) + 2 + 
		if  := len();  <  {
			return nil, fmt.Errorf("reading variation store: EOF: expected length: %d, got %d", , )
		}
		 := [.vstore+2 : ]
		.VarStore, _,  = tables.ParseItemVarStore()
		if  != nil {
			return nil, 
		}
	}
	return &, nil
}

func parseIndex2( []byte,  int) ([][]byte, error) {
	if  := len();  < +5 {
		return nil, fmt.Errorf("reading INDEX: EOF: expected length: %d, got %d", +5, )
	}
	var  indexStart
	.mustParse([:])
	, ,  := parseIndexContent([+5:], )
	return , 
}

type topDict2 struct {
	charStrings int32 // offset
	fdArray     int32 // offset
	fdSelect    int32 // offset
	vstore      int32 // offset
}

func ( *topDict2) () ps.Context { return ps.TopDict }

func ( *topDict2) ( *ps.Machine,  ps.Operator) error {
	switch  {
	case ps.Operator{Operator: 7, IsEscaped: true}: // FontMatrix
		// skip
		.ArgStack.Clear()
		return nil
	case ps.Operator{Operator: 17, IsEscaped: false}: // CharStrings
		if .ArgStack.Top < 1 {
			return fmt.Errorf("invalid number of arguments for operator %s in Top Dict", )
		}
		.charStrings = int32(.ArgStack.Pop())
	case ps.Operator{Operator: 36, IsEscaped: true}: // FDArray
		if .ArgStack.Top < 1 {
			return fmt.Errorf("invalid number of arguments for operator %s in Top Dict", )
		}
		.fdArray = int32(.ArgStack.Pop())
	case ps.Operator{Operator: 37, IsEscaped: true}: // FDSelect
		if .ArgStack.Top < 1 {
			return fmt.Errorf("invalid number of arguments for operator %s in Top Dict", )
		}
		.fdSelect = int32(.ArgStack.Pop())
	case ps.Operator{Operator: 24, IsEscaped: false}: // vstore
		if .ArgStack.Top < 1 {
			return fmt.Errorf("invalid number of arguments for operator %s in Top Dict", )
		}
		.vstore = int32(.ArgStack.Pop())
	default:
		return fmt.Errorf("invalid operator %s in Top Dict", )
	}
	return nil
}

type fontDict2 struct {
	privateDictSize   int32
	privateDictOffset int32
}

func ( *fontDict2) () ps.Context { return ps.TopDict }

func ( *fontDict2) ( *ps.Machine,  ps.Operator) error {
	switch  {
	case ps.Operator{Operator: 18, IsEscaped: false}: // Private
		if .ArgStack.Top < 2 {
			return fmt.Errorf("invalid number of arguments for operator %s in Font Dict", )
		}
		.privateDictOffset = int32(.ArgStack.Pop())
		.privateDictSize = int32(.ArgStack.Pop())
		return nil
	default:
		return fmt.Errorf("invalid operator %s in Font Dict", )
	}
}

// privateDict2 contains fields specific to the Private DICT context.
type privateDict2 struct {
	subrsOffset int32
	vsindex     int32 // 	itemVariationData index in the VariationStore structure table.
}

func (privateDict2) () ps.Context { return ps.PrivateDict }

// The Private DICT operators are defined by 5176.CFF.pdf Table 23 "Private
// DICT Operators".
func ( *privateDict2) ( *ps.Machine,  ps.Operator) error {
	if !.IsEscaped { // 1-byte operators.
		switch .Operator {
		case 6, 7, 8, 9: // "BlueValues" "OtherBlues" "FamilyBlues" "FamilyOtherBlues"
			return .ArgStack.PopN(-2)
		case 10, 11: // "StdHW" "StdVW"
			return .ArgStack.PopN(1)
		case 19: // "Subrs" pop 1
			if .ArgStack.Top < 1 {
				return errors.New("invalid stack size for 'subrs' in private Dict charstring")
			}
			.subrsOffset = int32(.ArgStack.Pop())
			return nil
		case 22: // "vsindex"
			if .ArgStack.Top < 1 {
				return fmt.Errorf("invalid stack size for %s in private Dict", )
			}
			.vsindex = int32(.ArgStack.Pop())
			return nil
		case 23: // "blend"
			return nil
		}
	} else { // 2-byte operators. The first byte is the escape byte.
		switch .Operator {
		case 9, 10, 11, 17, 18: // "BlueScale" "BlueShift" "BlueFuzz" "LanguageGroup" "ExpansionFactor"
			return .ArgStack.PopN(1)
		case 12, 13: //  "StemSnapH"  "StemSnapV"
			return .ArgStack.PopN(-2)
		}
	}
	return errors.New("invalid operand in private Dict charstring")
}