package geom

import (
	//"fmt"
	"image/color"
	"math/rand"

	"github.com/hajimehoshi/ebiten/v2"
)

var defaultSquareSpacing = 5
var minBorder = 20
var ScoreOffset = 50
var HighScoreOffset = 10
var GameGrid Grid
var MaxGridDimension = 20

type Grid struct {
	squareSpacing           int
	squares                 [][]Square
	startX                  int
	startY                  int
	squareSize              int
	coloredSquaresRemaining int
}

// used for comparing two squares positions
type Pair struct {
	rowNum int
	colNum int
}

func (pr Pair) equal(pr2 Pair) bool {
	return pr.rowNum == pr2.rowNum && pr.colNum == pr2.colNum

}

func pairAlreadyExists(pr Pair, pairs []Pair) bool {
	result := false
	for _, nextPair := range pairs {
		if pr.equal(nextPair) {
			return true
		}
	}

	return result
}

func (grd *Grid) NumSquares() int {
	size := -1
	if len(grd.squares[0]) != 0 {
		size = len(grd.squares) * len(grd.squares[0])
	}
	return size
}

// returns num rows by  num columns
func (grd *Grid) Size() (int, int) {
	if len(grd.squares[0]) != 0 {
		return len(grd.squares), len(grd.squares[0])
	}
	return 0, 0
}

func BuildGrid(numRows int, numColumns int) {
	grd := Grid{squareSpacing: defaultSquareSpacing}

	winWidth, winHeight := ebiten.WindowSize()
	sqSize := min((winWidth-2*minBorder-defaultSquareSpacing*numColumns)/numColumns, (winHeight-2*minBorder-ScoreOffset-3*HighScoreOffset-defaultSquareSpacing*numRows)/numRows)
	sqSize = max(sqSize, 1) //ensure a nonzero, nonnegative square size
	gridWidth := numColumns*(sqSize+grd.squareSpacing) - grd.squareSpacing
	gridHeight := numRows*(sqSize+grd.squareSpacing) - grd.squareSpacing

	currentX := (winWidth - gridWidth) / 2
	currentY := ScoreOffset + (winHeight-gridHeight-ScoreOffset-HighScoreOffset)/2
	grd.startX = currentX
	grd.startY = currentY
	grd.squareSize = sqSize

	table := make([][]Square, numRows)

	for i := 0; i < numRows; i++ {
		row := make([]Square, numColumns)
		for j := 0; j < numColumns; j++ {
			sq := MakeSquare(sqSize)
			sq.SetPosition(currentX, currentY)
			row[j] = sq
			currentX += sqSize + grd.squareSpacing
		}
		table[i] = row
		currentY += sqSize + grd.squareSpacing
		currentX = grd.startX
	}

	grd.squares = table

	//assign random colours to a subset of all squares
	squaresToColour := min(numColumns, numRows)
	usedPositions := make([]Pair, squaresToColour)
	coloredSoFar := 0
	for i := 0; i < squaresToColour; i++ {
		colourIndex := rand.Intn(len(LightRGBMap))
		row := rand.Intn(numRows) + 1
		col := rand.Intn(numColumns) + 1
		//prevent a colour on the starting square
		if row == numRows && col == 1 {
			col = 2
		}

		//make sure we don't colour the same square twice
		nextPair := Pair{rowNum: row, colNum: col}
		if coloredSoFar == 0 {
			usedPositions[0] = nextPair
			coloredSoFar = 1
		} else {
			for pairAlreadyExists(nextPair, usedPositions) {
				row = rand.Intn(numRows) + 1
				col = rand.Intn(numColumns) + 1
				//prevent a colour on the starting square
				if row == numRows && col == 1 {
					col = 2
				}
				nextPair = Pair{rowNum: row, colNum: col}
			}
			usedPositions[coloredSoFar] = nextPair
			coloredSoFar += 1

		}

		grd.ChangeColour(row, col, LightRGBMap[colourIndex])
		grd.GetSquareAt(row, col).RGBColourIndex = colourIndex
	}
	grd.coloredSquaresRemaining = squaresToColour

	GameGrid = grd
}

func (grd *Grid) Draw(screen *ebiten.Image) {
	opts := &ebiten.DrawImageOptions{}

	for _, rowOfSquares := range grd.squares {
		for _, sq := range rowOfSquares {
			xPos := float64(sq.X())
			yPos := float64(sq.Y())
			opts.GeoM.Translate(xPos, yPos)
			screen.DrawImage(sq.Img, opts)
			opts.GeoM.Reset()
		}
	}
}

func (grd *Grid) DrawUpTo(finalSquareCount int, screen *ebiten.Image) {
	opts := &ebiten.DrawImageOptions{}
	curr := 1
	for _, rowOfSquares := range grd.squares {
		for _, sq := range rowOfSquares {
			xPos := float64(sq.X())
			yPos := float64(sq.Y())
			opts.GeoM.Translate(xPos, yPos)
			screen.DrawImage(sq.Img, opts)
			opts.GeoM.Reset()
			if curr == finalSquareCount {
				return
			}
			curr++
		}
	}
}

func (grd *Grid) ChangeColour(row int, column int, clr color.Color) {
	sq := grd.squares[row-1][column-1]
	sq.ChangeColour(clr)
}

func (grd *Grid) GetSquareAt(row int, column int) *Square {
	return &grd.squares[row-1][column-1]
}

func (grd *Grid) ColouredSquaresRemaining() int {
	return grd.coloredSquaresRemaining
}
