// Copyright (c) 2022 Sam Blenny
// SPDX-License-Identifier: Apache-2.0 OR MIT
//
package lib

import (
	"fmt"
	"image"
	"strings"
)

// Holds packed XOR mask values of a blit pattern for character's glyph.
//
// Pixel order: row-major order traversal of px matrix; top-left pixel goes in
//              least significant bit of of .Words[1]
// Mask values: bit=1 means foreground, bit=0 means background
//
// Patterns that need padding because their size is not a multiple of 32 bits
// (width*height % 32 != 0) get padded with zeros in the least significant bits
// of the last word.
type BlitPattern struct {
	Words []uint32
	Width uint8
	CS    CharSpec
}

// Extract matrix of pixels from an image containing grid of glyphs
// - img: image.Image from png file containing glyph grid
// - font: Glyph sheet specs (glyph size, border/gutter, etc)
// - cs: Character specs (source row and column in glyph grid)
func NewBlitPattern(img image.Image, font FontSpec, cs CharSpec, dbg bool) BlitPattern {
	row := cs.Row
	col := cs.Col
	imgRect := img.Bounds()
	rows := (imgRect.Max.Y - font.Border) / (font.Size + font.Gutter)
	if row < 0 || row >= rows || col < 0 || col >= font.Cols {
		panic("row or column out of range")
	}
	// Get pixels for grid cell, converting from RGBA to 1-bit
	gridSize := font.Size + font.Gutter
	border := font.Border
	pxMatrix := Matrix{}
	for y := border + (row * gridSize); y < (row+1)*gridSize; y++ {
		var row MatrixRow
		for x := border + (col * gridSize); x < (col+1)*gridSize; x++ {
			r, _, _, _ := img.At(x, y).RGBA()
			//fmt.Println(r, g, b, a)
			if r == 0 {
				row = append(row, 1)
			} else {
				row = append(row, 0)
			}
		}
		pxMatrix = append(pxMatrix, row)
	}
	pxMatrix = pxMatrix.Trim(font, row, col)
	width := uint8(len(pxMatrix[0]))
	if font.Size <= 16 {
		pxMatrix = pxMatrix.padTo16x16()
	} else {
		pxMatrix = pxMatrix.padTo32x32()
	}
	pxMatrix.Debug(cs, dbg)
	if font.Size <= 16 {
		patternBytes := pxMatrix.convertToPattern()
		return BlitPattern{patternBytes, width, cs}
	} else {
		patternBytes := pxMatrix.convertToPattern32()
		return BlitPattern{patternBytes, width, cs}
	}
}

// Convert blit pattern to rust source code for part of an array of bytes
func ConvertPatternToRust(pattern BlitPattern) string {
	patternStr := "    "
	wordsPerRow := uint32(8)
	ceilRow := uint32(len(pattern.Words)) / wordsPerRow
	if uint32(len(pattern.Words))%wordsPerRow > 0 {
		ceilRow += 1
	}
	for i := uint32(0); i < ceilRow; i++ {
		if i != 0 {
			patternStr += "    "
		}
		start := i * wordsPerRow
		end := min(uint32(len(pattern.Words)), (i+1)*wordsPerRow)
		line := pattern.Words[start:end]
		var s []string
		for _, word := range line {
			s = append(s, fmt.Sprintf("0x%08x", word))
		}
		patternStr += strings.Join(s, ", ") + ",\n"
	}
	return patternStr
}

// Return lowest value among two integers
func min(a uint32, b uint32) uint32 {
	if b > a {
		return a
	}
	return b
}
