package scrabble

import (
	"gitea.iliadenisov.ru/developer/scrabble-solver/board"
	"gitea.iliadenisov.ru/developer/scrabble-solver/rack"
	"gitea.iliadenisov.ru/developer/scrabble-solver/rules"
)

// generateBoth runs an across-generator on the board (for horizontal plays) and on its
// transpose (for vertical plays), as selected by mode, then scores and de-duplicates the
// results. runAcross reports placements in the coordinates of the board it is given; for
// the transpose pass they are mapped back to the real board.
func generateBoth(b *board.Board, rs *rules.Ruleset, rk rack.Rack, mode Mode, opts PlayOptions,
	runAcross func(bd *board.Board, rk rack.Rack, opts PlayOptions, emit func([]Placement))) []Move {

	rk = rk.Clone() // generation mutates the rack in place and restores it
	var moves []Move
	seen := make(map[string]struct{})
	emit := func(dir Direction, placements []Placement) {
		key := moveKey(dir, placements)
		if _, dup := seen[key]; dup {
			return
		}
		m, err := EvaluateOpts(b, rs, dir, placements, opts)
		if err != nil {
			return
		}
		seen[key] = struct{}{}
		moves = append(moves, m)
	}

	if mode.Includes(Horizontal) {
		runAcross(b, rk, opts, func(p []Placement) { emit(Horizontal, p) })
	}
	if mode.Includes(Vertical) {
		tb := b.Transpose()
		runAcross(tb, rk, opts, func(p []Placement) {
			rp := make([]Placement, len(p))
			for i, pl := range p {
				rp[i] = Placement{Row: pl.Col, Col: pl.Row, Letter: pl.Letter, Blank: pl.Blank}
			}
			emit(Vertical, rp)
		})
	}
	return moves
}

// centerFor returns the centre square in bd's coordinates. bd is either the real board
// or its transpose; the ruleset stores the centre on the real board.
func centerFor(bd *board.Board, rs *rules.Ruleset) (row, col int) {
	r, c := rs.Center/rs.Cols, rs.Center%rs.Cols
	if bd.Rows() == rs.Rows && bd.Cols() == rs.Cols {
		return r, c
	}
	return c, r // transposed
}
