package main

import (
	"fmt"
	"image"
	"image/color"
	"strings"

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

func parseFAIcon(ref string) (faStyle, string, error) {
	if !strings.HasPrefix(ref, "fa") {
		return 0, "", fmt.Errorf("not a font awesome ref")
	}

	var style faStyle
	var name string

	switch {
	case strings.HasPrefix(ref, "fab:"):
		style = faBrands
		name = strings.TrimPrefix(ref, "fab:")
	case strings.HasPrefix(ref, "far:"):
		style = faRegular
		name = strings.TrimPrefix(ref, "far:")
	case strings.HasPrefix(ref, "fa:"):
		style = faSolid
		name = strings.TrimPrefix(ref, "fa:")
	default:
		return 0, "", fmt.Errorf("invalid font awesome ref: %s", ref)
	}

	if name == "" {
		return 0, "", fmt.Errorf("empty icon name")
	}

	return style, name, nil
}

func faCodepoint(style faStyle, name string) (rune, error) {
	m, ok := faCodepoints[style]
	if !ok {
		return 0, fmt.Errorf("unknown style")
	}

	if cp, ok := m[name]; ok {
		return cp, nil
	}

	if cp, ok := m[normalizeFAName(name)]; ok {
		return cp, nil
	}

	return 0, fmt.Errorf("icon %q not found", name)
}

func normalizeFAName(name string) string {
	if len(name) == 0 {
		return name
	}
	parts := strings.Split(name, "-")
	for i, p := range parts {
		if len(p) > 0 {
			parts[i] = strings.ToUpper(p[:1]) + p[1:]
		}
	}
	camel := strings.Join(parts, "")
	return strings.ToLower(camel[:1]) + camel[1:]
}

func faFontBytes(style faStyle) ([]byte, error) {
	return faFonts.ReadFile(style.otfPath())
}

func loadFAFace(style faStyle, pointSize float64) (font.Face, error) {
	data, err := faFontBytes(style)
	if err != nil {
		return nil, fmt.Errorf("read font: %w", err)
	}

	fnt, err := opentype.Parse(data)
	if err != nil {
		return nil, fmt.Errorf("parse font: %w", err)
	}

	face, err := opentype.NewFace(fnt, &opentype.FaceOptions{
		Size:    pointSize,
		DPI:     72,
		Hinting: font.HintingFull,
	})
	if err != nil {
		return nil, fmt.Errorf("new face: %w", err)
	}
	return face, nil
}

func renderFAGlyph(style faStyle, name string, size int, scale float64) (image.Image, error) {
	cp, err := faCodepoint(style, name)
	if err != nil {
		return nil, err
	}

	if scale <= 0 {
		scale = 0.55
	}
	fontSize := float64(size) * scale
	face, err := loadFAFace(style, fontSize)
	if err != nil {
		return nil, err
	}
	defer face.Close()

	rgba := image.NewRGBA(image.Rect(0, 0, size, size))

	adv := font.MeasureString(face, string(cp)).Ceil()
	offX := (size - adv) / 2
	if offX < 0 {
		offX = 0
	}

	metrics := face.Metrics()
	baselineY := (size + metrics.Ascent.Ceil() - metrics.Descent.Ceil()) / 2

	d := font.Drawer{
		Dst:  rgba,
		Src:  image.NewUniform(color.White),
		Face: face,
		Dot:  fixed.P(offX, baselineY),
	}
	d.DrawString(string(cp))

	return rgba, nil
}

func isFAIconRef(path string) bool {
	return strings.HasPrefix(path, "fa:") || strings.HasPrefix(path, "far:") || strings.HasPrefix(path, "fab:")
}

