// Copyright ©2021 The Gonum 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 font

import (
	
	
	

	
	
	
	
)

// DefaultCache is the global cache for fonts.
var DefaultCache *Cache = NewCache(nil)

// Font represents a font face.
type Font struct {
	// Typeface identifies the Font.
	Typeface Typeface

	// TODO(sbinet): Gio@v0.2.0 has dropped font.Font.Variant
	// we should probably follow suit.

	// Variant is the variant of a font, such as "Mono" or "Smallcaps".
	Variant Variant

	// Style is the style of a font, such as Regular or Italic.
	Style font.Style

	// Weight is the weight of a font, such as Normal or Bold.
	Weight font.Weight

	// Size is the size of the font.
	Size Length
}

// Name returns a fully qualified name for the given font.
func ( *Font) () string {
	 := .Variant
	 := weightName(.Weight)
	 := styleName(.Style)

	switch .Style {
	case font.StyleNormal:
		 = ""
		if .Weight == font.WeightNormal {
			 = "Regular"
		}
	default:
		if .Weight == font.WeightNormal {
			 = ""
		}
	}

	return fmt.Sprintf("%s%s-%s%s", .Typeface, , , )
}

// From returns a copy of the provided font with its size set.
func ( Font,  Length) Font {
	 := 
	.Size = 
	return 
}

// Typeface identifies a particular typeface design.
// The empty string denotes the default typeface.
type Typeface string

// Variant denotes a typeface variant, such as "Mono", "Smallcaps" or "Math".
type Variant string

// Extents contains font metric information.
type Extents struct {
	// Ascent is the distance that the text
	// extends above the baseline.
	Ascent Length

	// Descent is the distance that the text
	// extends below the baseline. The descent
	// is given as a positive value.
	Descent Length

	// Height is the distance from the lowest
	// descending point to the highest ascending
	// point.
	Height Length
}

// Face holds a font descriptor and the associated font face.
type Face struct {
	Font Font
	Face *opentype.Font
}

// Name returns a fully qualified name for the given font.
func ( *Face) () string {
	return .Font.Name()
}

// FontFace returns the opentype font face for the requested
// dots-per-inch resolution.
func ( *Face) ( float64) font.Face {
	,  := opentype.NewFace(.Face, &opentype.FaceOptions{
		Size: .Font.Size.Points(),
		DPI:  ,
	})
	if  != nil {
		panic()
	}
	return 
}

// default hinting for OpenType fonts
const defaultHinting = font.HintingNone

// Extents returns the FontExtents for a font.
func ( *Face) () Extents {
	var (
		// TODO(sbinet): re-use a Font-level sfnt.Buffer instead?
		  sfnt.Buffer
		 = fixed.Int26_6(.Face.UnitsPerEm())
	)

	,  := .Face.Metrics(&, , defaultHinting)
	if  != nil {
		panic(fmt.Errorf("could not extract font extents: %v", ))
	}
	 := .Font.Size / Points(float64())
	return Extents{
		Ascent:  Points(float64(.Ascent)) * ,
		Descent: Points(float64(.Descent)) * ,
		Height:  Points(float64(.Height)) * ,
	}
}

// Width returns width of a string when drawn using the font.
func ( *Face) ( string) Length {
	var (
		 = fixed.Int26_6(.Face.UnitsPerEm())

		// scale converts sfnt.Unit to float64
		 = .Font.Size / Points(float64())

		     = 0
		   = false
		       sfnt.Buffer
		,  sfnt.GlyphIndex
		   = defaultHinting
	)
	for ,  := range  {
		var  error
		,  = .Face.GlyphIndex(&, )
		if  != nil {
			panic(fmt.Errorf("could not get glyph index: %v", ))
		}
		if  {
			,  := .Face.Kern(&, , , , )
			switch {
			case  == nil:
				 += int()
			case errors.Is(, sfnt.ErrNotFound):
				// no-op
			default:
				panic(fmt.Errorf("could not get kerning: %v", ))
			}
		}
		,  := .Face.GlyphAdvance(&, , , )
		if  != nil {
			panic(fmt.Errorf("could not retrieve glyph's advance: %v", ))
		}
		 += int()
		,  = , true
	}
	return Points(float64()) * 
}

// Collection is a collection of fonts, regrouped under a common typeface.
type Collection []Face

// Cache collects font faces.
type Cache struct {
	mu    sync.RWMutex
	def   Typeface
	faces map[Font]*opentype.Font
}

// We make Cache implement dummy GobDecoder and GobEncoder interfaces
// to allow plot.Plot (or any other type holding a Cache) to be (de)serialized
// with encoding/gob.
// As Cache holds opentype.Font, the reflect-based gob (de)serialization can not
// work: gob isn't happy with opentype.Font having no exported field:
//
//   error: gob: type font.Cache has no exported fields
//
// FIXME(sbinet): perhaps encode/decode Cache.def typeface?

func ( *Cache) () ([]byte, error) { return nil, nil }
func ( *Cache) ([]byte) error {
	if .faces == nil {
		.faces = make(map[Font]*opentype.Font)
	}
	return nil
}

// NewCache creates a new cache of fonts from the provided collection of
// font Faces.
// The first font Face in the collection is set to be the default one.
func ( Collection) *Cache {
	 := &Cache{
		faces: make(map[Font]*opentype.Font, len()),
	}
	.Add()
	return 
}

// Add adds a whole collection of font Faces to the font cache.
// If the cache is empty, the first font Face in the collection is set
// to be the default one.
func ( *Cache) ( Collection) {
	.mu.Lock()
	defer .mu.Unlock()

	if .faces == nil {
		.faces = make(map[Font]*opentype.Font, len())
	}
	for ,  := range  {
		if  == 0 && .def == "" {
			.def = .Font.Typeface
		}
		 := .Font
		.Size = 0 // store all font descriptors with the same size.
		.faces[] = .Face
	}
}

// Lookup returns the font Face corresponding to the provided Font descriptor,
// with the provided font size set.
//
// If no matching font Face could be found, the one corresponding to
// the default typeface is selected and returned.
func ( *Cache) ( Font,  Length) Face {
	.mu.RLock()
	defer .mu.RUnlock()

	if len(.faces) == 0 {
		return Face{}
	}

	 := .lookup()
	if  == nil {
		.Typeface = .def
		 = .lookup()
	}

	 := Face{
		Font: ,
		Face: ,
	}
	.Font.Size = 
	return 
}

// Has returns whether the cache contains the exact font descriptor.
func ( *Cache) ( Font) bool {
	.mu.RLock()
	defer .mu.RUnlock()

	 := .lookup()
	return  != nil
}

func ( *Cache) ( Font) *opentype.Font {
	.Size = 0

	 := .faces[]
	if  == nil {
		 := 
		.Weight = font.WeightNormal
		 = .faces[]
	}
	if  == nil {
		 := 
		.Style = font.StyleNormal
		 = .faces[]
	}
	if  == nil {
		 := 
		.Style = font.StyleNormal
		.Weight = font.WeightNormal
		 = .faces[]
	}

	return 
}

func weightName( font.Weight) string {
	switch  {
	case font.WeightThin:
		return "Thin"
	case font.WeightExtraLight:
		return "ExtraLight"
	case font.WeightLight:
		return "Light"
	case font.WeightNormal:
		return "Regular"
	case font.WeightMedium:
		return "Medium"
	case font.WeightSemiBold:
		return "SemiBold"
	case font.WeightBold:
		return "Bold"
	case font.WeightExtraBold:
		return "ExtraBold"
	case font.WeightBlack:
		return "Black"
	}
	return fmt.Sprintf("weight(%d)", )
}

func styleName( font.Style) string {
	switch  {
	case font.StyleNormal:
		return "Normal"
	case font.StyleItalic:
		return "Italic"
	case font.StyleOblique:
		return "Oblique"
	}
	return fmt.Sprintf("style(%d)", )
}