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

package font

import (
	
	
	

	ot 
	
)

// sbix

type sbix []tables.Strike

func newSbix( tables.Sbix) sbix { return .Strikes }

// chooseStrike selects the best match for the given resolution.
// It returns nil only if the table is empty
func ( sbix) (,  uint16) *tables.Strike {
	if len() == 0 {
		return nil
	}

	 := maxu16(, )
	if  == 0 {
		 = math.MaxUint16 // choose largest strike
	}

	var (
		 = 0
		  = [0].Ppem
	)
	for ,  := range  {
		 := .Ppem
		if  <=  &&  <  ||  >  &&  >  {
			 = 
			 = 
		}
	}
	return &[]
}

func ( sbix) ( *tables.Hhea, ,  uint16) []BitmapSize {
	 := make([]BitmapSize, 0, len())
	for ,  := range  {
		 := strikeSizeMetrics(, , , )
		// only use strikes with valid PPEM values
		if .XPpem == 0 || .YPpem == 0 {
			continue
		}
		 = append(, )
	}
	return 
}

func strikeSizeMetrics( tables.Strike,  *tables.Hhea, ,  uint16) ( BitmapSize) {
	.XPpem, .YPpem = .Ppem, .Ppem
	.Height = mulDiv(uint16(.Ascender-.Descender+.LineGap), .Ppem, )

	inferBitmapWidth(&, , )

	return 
}

// ---------------------------- bitmap ----------------------------

func loadBitmap( *ot.Loader, ,  ot.Tag) (bitmap, error) {
	,  := .RawTable()
	if  != nil {
		return nil, 
	}
	, ,  := tables.ParseCBLC()
	if  != nil {
		return nil, 
	}
	,  := .RawTable()
	if  != nil {
		return nil, 
	}
	return newBitmap(, )
}

// CBLC/CBDT or EBLC/EBDT or BLOC/BDAT
type bitmap []bitmapStrike

func newBitmap( tables.EBLC,  []byte) (bitmap, error) {
	 := make(bitmap, len(.BitmapSizes))
	for ,  := range .BitmapSizes {
		 := .IndexSubTables[]
		[] = bitmapStrike{
			subTables: make([]bitmapSubtable, len()),
			hori:      .Hori,
			vert:      .Vert,
			ppemX:     uint16(.PpemX),
			ppemY:     uint16(.PpemY),
		}
		for ,  := range  {
			var  error
			[].subTables[],  = newBitmapSubtable(, )
			if  != nil {
				return nil, 
			}
		}
	}
	return , nil
}

func ( bitmap) (,  uint16) []BitmapSize {
	 := make([]BitmapSize, 0, len())
	for ,  := range  {
		 := .sizeMetrics(, )
		// only use strikes with valid PPEM values
		if .XPpem == 0 || .YPpem == 0 {
			continue
		}
		 = append(, )
	}
	return 
}

type bitmapStrike struct {
	subTables    []bitmapSubtable
	hori, vert   tables.SbitLineMetrics
	ppemX, ppemY uint16
}

// chooseStrike selects the best match for the given resolution.
// It returns nil only if the table is empty
func ( bitmap) (,  uint16) *bitmapStrike {
	if len() == 0 {
		return nil
	}
	 := maxu16(, )
	if  == 0 {
		 = math.MaxUint16 // choose largest strike
	}
	var (
		 = 0
		  = maxu16([0].ppemX, [0].ppemY)
	)
	for ,  := range  {
		 := maxu16(.ppemX, .ppemY)
		if  <=  &&  <  ||  >  &&  >  {
			 = 
			 = 
		}
	}
	return &[]
}

func ( *bitmapStrike) (,  uint16) ( BitmapSize) {
	.XPpem, .YPpem = .ppemX, .ppemY
	 := int16(.hori.Ascender)
	 := int16(.hori.Descender)

	 := .hori.MaxBeforeBL
	 := .hori.MinAfterBL

	/* Due to fuzzy wording in the EBLC documentation, we find both */
	/* positive and negative values for `descender'.  Additionally, */
	/* many fonts have both `ascender' and `descender' set to zero  */
	/* (which is definitely wrong).  MS Windows simply ignores all  */
	/* those values...  For these reasons we apply some heuristics  */
	/* to get a reasonable, non-zero value for the height.          */

	if  > 0 {
		if  < 0 {
			 = -
		}
	} else if  == 0 {
		if  == 0 {
			/* sanitize buggy ascender and descender values */
			if  != 0 ||  != 0 {
				 = int16()
				 = int16()
			} else {
				 = int16(.YPpem)
				 = 0
			}
		}
	}

	if  :=  - ;  > 0 {
		.Height = uint16()
	} else {
		.Height = .YPpem
	}

	inferBitmapWidth(&, , )

	return 
}

func inferBitmapWidth( *BitmapSize, ,  uint16) {
	.Width = uint16((uint32()*uint32(.XPpem) + uint32(/2)) / uint32())
}

// return nil when not found
func ( *bitmapStrike) ( gID) *bitmapSubtable {
	for ,  := range .subTables {
		if .first <=  &&  <= .last {
			return &.subTables[]
		}
	}
	return nil
}

type bitmapSubtable struct {
	first       gID // First glyph ID of this range.
	last        gID // Last glyph ID of this range (inclusive).
	imageFormat uint16
	index       bitmapIndex
}

func newBitmapSubtable( tables.BitmapSubtable,  []byte) (bitmapSubtable, error) {
	 := bitmapSubtable{
		first:       .FirstGlyph,
		last:        .LastGlyph,
		imageFormat: .ImageFormat,
	}
	if ,  := len(), int(.ImageDataOffset);  <  {
		return bitmapSubtable{}, errors.New("invalid bitmap table (EOF)")
	}
	 := [.ImageDataOffset:]

	var  error
	switch index := .IndexData.(type) {
	case tables.IndexData1:
		.index,  = parseIndexSubTable1(, , )
	case tables.IndexData2:
		.index,  = parseIndexSubTable2(, , )
	case tables.IndexData3:
		.index,  = parseIndexSubTable3(, , )
	case tables.IndexData4:
		.index,  = parseIndexSubTable4(, , )
	case tables.IndexData5:
		.index,  = parseIndexSubTable5(, , )
	}
	return , 
}

func ( *bitmapSubtable) ( gID) *bitmapImage {
	return .index.imageFor(, .first, .last)
}

type bitmapIndex interface {
	// first, last is the range of the subtable
	imageFor(glyph gID, first, last gID) *bitmapImage
}

type bitmapImage struct {
	image   []byte
	metrics tables.SmallGlyphMetrics
}

type indexSubTable1And3 struct {
	// length lastGlyph - firstGlyph + 1, elements may be nil
	glyphs []bitmapImage
	format uint16
}

func ( indexSubTable1And3) ( gID, ,  gID) *bitmapImage {
	if  <  ||  >  {
		return nil
	}
	return &.glyphs[-]
}

// imageData starts at the image (table[imageDataOffset:])
func parseIndexSubTable1( tables.BitmapSubtable,  tables.IndexData1,  []byte) (indexSubTable1And3, error) {
	 := indexSubTable1And3{
		format: .ImageFormat,
		glyphs: make([]bitmapImage, len(.SbitOffsets)-1),
	}
	for  := range .glyphs {
		if .SbitOffsets[] == .SbitOffsets[+1] {
			continue
		}
		var  error
		.glyphs[],  = parseBitmapDataMetrics(, .SbitOffsets[], .SbitOffsets[+1], .ImageFormat)
		if  != nil {
			return , fmt.Errorf("invalid bitmap index format 1: %s", )
		}
	}
	return , nil
}

func parseIndexSubTable3( tables.BitmapSubtable,  tables.IndexData3,  []byte) (indexSubTable1And3, error) {
	 := indexSubTable1And3{
		format: .ImageFormat,
		glyphs: make([]bitmapImage, len(.SbitOffsets)-1),
	}
	for  := range .glyphs {
		if .SbitOffsets[] == .SbitOffsets[+1] {
			continue
		}
		var  error
		.glyphs[],  = parseBitmapDataMetrics(, tables.Offset32(.SbitOffsets[]), tables.Offset32(.SbitOffsets[+1]), .ImageFormat)
		if  != nil {
			return , fmt.Errorf("invalid bitmap index format 1: %s", )
		}
	}
	return , nil
}

type bitmapDataStandalone []byte

type indexSubTable2 struct {
	glyphs  []bitmapDataStandalone
	format  uint16
	metrics tables.BigGlyphMetrics
}

func ( indexSubTable2) ( gID, ,  gID) *bitmapImage {
	if  <  ||  >  {
		return nil
	}
	return &bitmapImage{image: .glyphs[-], metrics: .metrics.SmallGlyphMetrics}
}

// imageData starts at the image (table[imageDataOffset:])
func parseIndexSubTable2( tables.BitmapSubtable,  tables.IndexData2,  []byte) (indexSubTable2, error) {
	 := indexSubTable2{
		format:  .ImageFormat,
		metrics: .BigMetrics,
		glyphs:  make([]bitmapDataStandalone, int(.LastGlyph)-int(.FirstGlyph)+1),
	}
	for  := range .glyphs {
		var  error
		.glyphs[],  = parseBitmapDataStandalone(, .ImageSize*uint32(), .ImageSize*uint32(+1), .ImageFormat)
		if  != nil {
			return , fmt.Errorf("invalid bitmap index format 2: %s", )
		}
	}
	return , nil
}

type indexedBitmapGlyph struct {
	data  bitmapImage
	glyph gID
}

type indexSubTable4 struct {
	glyphs []indexedBitmapGlyph
	format uint16
}

func ( indexSubTable4) ( gID, ,  gID) *bitmapImage {
	if  <  ||  >  {
		return nil
	}
	for ,  := range .glyphs {
		if .glyph ==  {
			return &.glyphs[].data
		}
	}
	return nil
}

// imageData starts at the image (table[imageDataOffset:])
func parseIndexSubTable4( tables.BitmapSubtable,  tables.IndexData4,  []byte) (indexSubTable4, error) {
	 := indexSubTable4{
		format: .ImageFormat,
		glyphs: make([]indexedBitmapGlyph, len(.GlyphArray)-1),
	}
	for  := range .glyphs {
		,  := .GlyphArray[], .GlyphArray[+1]
		.glyphs[].glyph = .GlyphID
		var  error
		.glyphs[].data,  = parseBitmapDataMetrics(, tables.Offset32(.SbitOffset), tables.Offset32(.SbitOffset), .ImageFormat)
		if  != nil {
			return , fmt.Errorf("invalid bitmap index format 4: %s", )
		}
	}
	return , nil
}

type indexSubTable5 struct {
	glyphIndexes []gID                  // sorted by glyph index
	glyphs       []bitmapDataStandalone // corresponding to glyphIndexes
	format       uint16
	metrics      tables.BigGlyphMetrics
}

func ( indexSubTable5) ( gID, ,  gID) *bitmapImage {
	if  <  ||  >  {
		return nil
	}
	// binary search
	for ,  := 0, len(.glyphIndexes);  < ; {
		 :=  + (-)/2
		 := .glyphIndexes[]
		if  <  {
			 = 
		} else if  <  {
			 =  + 1
		} else {
			return &bitmapImage{image: .glyphs[], metrics: .metrics.SmallGlyphMetrics}
		}
	}
	return nil
}

// imageData starts at the image (table[imageDataOffset:])
func parseIndexSubTable5( tables.BitmapSubtable,  tables.IndexData5,  []byte) (indexSubTable5, error) {
	 := indexSubTable5{
		format:       .ImageFormat,
		metrics:      .BigMetrics,
		glyphIndexes: .GlyphIdArray,
		glyphs:       make([]bitmapDataStandalone, len(.GlyphIdArray)),
	}

	for  := range .glyphs {
		var  error
		.glyphs[],  = parseBitmapDataStandalone(, .ImageSize*uint32(), (.ImageSize+1)*uint32(), .ImageFormat)
		if  != nil {
			return , fmt.Errorf("invalid bitmap index format 5: %s", )
		}
	}
	return , nil
}

func parseBitmapDataMetrics( []byte, ,  tables.Offset32,  uint16) (bitmapImage, error) {
	if len() < int() ||  >  {
		return bitmapImage{}, errors.New("invalid bitmap data table (EOF)")
	}
	 = [:]
	switch  {
	case 1, 6, 7, 8, 9:
		return bitmapImage{}, fmt.Errorf("valid but currently not implemented bitmap image format: %d", )
	case 2:
		, ,  := tables.ParseBitmapData2()
		return bitmapImage{metrics: .SmallGlyphMetrics, image: .Image}, 
	case 17:
		, ,  := tables.ParseBitmapData17()
		return bitmapImage{metrics: .SmallGlyphMetrics, image: .Image}, 
	case 18:
		, ,  := tables.ParseBitmapData18()
		return bitmapImage{metrics: .SmallGlyphMetrics, image: .Image}, 
	default:
		return bitmapImage{}, fmt.Errorf("unsupported bitmap image format: %d", )
	}
}

func parseBitmapDataStandalone( []byte, ,  uint32,  uint16) (bitmapDataStandalone, error) {
	if len() < int() ||  >  {
		return nil, fmt.Errorf("invalid bitmap data table (EOF for [%d,%d])", , )
	}
	 = [:]
	switch  {
	case 4:
		return nil, fmt.Errorf("valid but currently not implemented bitmap image format: %d", )
	case 5:
		, ,  := tables.ParseBitmapData5()
		return .Image, 
	case 19:
		, ,  := tables.ParseBitmapData19()
		return .Image, 
	default:
		return nil, fmt.Errorf("unsupported bitmap image format: %d", )
	}
}

func maxu16(,  uint16) uint16 {
	if  >  {
		return 
	}
	return 
}

func mulDiv(, ,  uint16) uint16 {
	return uint16(uint32() * uint32() / uint32())
}