// 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

/*
This file contains opt-in tests for kerning in user provided fonts.

Kerning information in kern and GPOS tables can be quite complex. These tests
recursively load all fonts from -bulkFontDirs and try to kern all possible
glyph pairs.

These tests only check if there are no errors during kerning. Tests of actual
kerning values are in proprietary_test.go.

Note: CJK fonts can contain billions of posible kerning pairs. Testing for
these fonts stops after -bulkMaxKernPairs.

To opt-in:

go test golang.org/x/image/font/sfnt -test.run=BulkKern -args -bulk -bulkFontDirs /Library/Fonts:./myfonts
*/

import (
	"flag"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"strings"
	"testing"

	"golang.org/x/image/font"
	"golang.org/x/image/math/fixed"
)

var (
	bulk = flag.Bool("bulk", false, "")

	fontDirs = flag.String(
		"bulkFontDirs",
		"./",
		"separated directories to search for fonts",
	)
	maxKernPairs = flag.Int(
		"bulkMaxKernPairs",
		20000000,
		"skip testing of kerning after this many tested pairs",
	)
)

func TestBulkKern(t *testing.T) {
	if !*bulk {
		t.Skip("skipping bulk font test")
	}

	for _, fontDir := range filepath.SplitList(*fontDirs) {
		err := filepath.Walk(fontDir, func(path string, info os.FileInfo, err error) error {
			if err != nil {
				return err
			}
			if info.IsDir() {
				return nil
			}
			if strings.HasSuffix(path, ".ttf") || strings.HasSuffix(path, ".otf") {
				t.Run(info.Name(), testFontKerning(filepath.Join(path)))
			}
			return nil
		})
		if err != nil {
			t.Fatal("error finding fonts", err)
		}
	}

}

func testFontKerning(fname string) func(*testing.T) {
	return func(t *testing.T) {
		t.Parallel()
		b, err := ioutil.ReadFile(fname)
		if err != nil {
			t.Fatal(err)
		}
		fnt, err := Parse(b)
		if err != nil {
			t.Fatal(err)
		}

		buf := &Buffer{}

		// collect all GlyphIndex
		glyphs := make([]GlyphIndex, 1, fnt.NumGlyphs())
		glyphs[0] = GlyphIndex(0)
		r := rune(0)
		for r < 0xffff {
			g, err := fnt.GlyphIndex(buf, r)
			r++
			if g == 0 || err == ErrNotFound {
				continue
			}
			if err != nil {
				t.Fatal(err)
			}
			glyphs = append(glyphs, g)
			if len(glyphs) == fnt.NumGlyphs() {
				break
			}
		}

		var kerned, tested int
		for _, g1 := range glyphs {
			for _, g2 := range glyphs {
				if tested >= *maxKernPairs {
					log.Printf("stop testing after %d or %d kerning pairs (found %d pairs)",
						tested, len(glyphs)*len(glyphs), kerned)
					return
				}

				tested++
				adv, err := fnt.Kern(buf, g1, g2, fixed.I(20), font.HintingNone)
				if err == ErrNotFound {
					continue
				}
				if err != nil {
					t.Fatal(err)
				}
				if adv != 0 {
					kerned++
				}
			}
		}

		log.Printf("found %d kerning pairs for %d glyphs (%.1f%%) in %q",
			kerned,
			len(glyphs),
			100*float64(kerned)/float64(len(glyphs)*len(glyphs)),
			fname,
		)
	}
}
