module main

import gg
import rand
import time

const cover = gg.rgba(85, 200, 85, 255)
const csize = 120 // cell size in pixels
const letters = 'AABBOOCCVVXXYYZZMMKKHHTT'.split('')
const header_size = 30

struct Cell {
mut:
	is_open bool
	letter  string
}

struct Game {
mut:
	ctx       &gg.Context = unsafe { nil }
	cells     []Cell
	card1_idx ?int
	card2_idx ?int
	size      int // in cells
	remaining int
	sw        time.StopWatch = time.new_stopwatch()
	revert_sw time.StopWatch = time.new_stopwatch(auto_start: false)
}

fn (mut g Game) restart() {
	ncells := g.size * g.size
	g.remaining = ncells
	g.cells = []Cell{len: ncells, init: Cell{
		letter: letters[index % letters.len]
	}}
	rand.shuffle(mut g.cells) or {}
	g.sw = time.new_stopwatch()
	g.card1_idx = none
	g.card2_idx = none
}

fn (mut g Game) draw_cell(i int, cell Cell) {
	x, y := i % g.size, i / g.size
	rect_x, rect_y := x * csize, header_size + y * csize
	if g.cells[i].is_open || g.sw.elapsed().milliseconds() <= 1000 {
		lsize := 96
		g.ctx.draw_rect_empty(rect_x + 6, rect_y + 6, csize - 10, csize - 10, gg.light_gray)
		g.ctx.draw_text(rect_x + csize / 2 - lsize / 3, rect_y + csize / 2 - lsize / 2,
			g.cells[i].letter, color: gg.yellow, size: lsize)
	} else {
		g.ctx.draw_rect_filled(rect_x + 6, rect_y + 6, csize - 10, csize - 10, cover)
	}
}

fn on_frame(mut g Game) {
	ws := gg.window_size()
	g.ctx.begin()
	message := '(r)estart (esc)ape | remaining: ${g.remaining:02} | time: ${f64(g.sw.elapsed().milliseconds()) / 1000.0:06.1f}s'
	g.ctx.draw_text(ws.width / 2, 7, message, color: gg.light_gray, size: 22, align: .center)
	for i, cell in g.cells {
		g.draw_cell(i, cell)
	}
	if g.revert_sw.elapsed().milliseconds() > 750 {
		g.revert_sw = time.new_stopwatch(auto_start: false)
		if g.card1_idx != none {
			if g.card2_idx != none {
				g.cells[g.card1_idx].is_open = false
				g.cells[g.card2_idx].is_open = false
				g.card1_idx = none
				g.card2_idx = none
				g.remaining = g.cells.count(!it.is_open)
			}
		}
	}
	g.ctx.end()
}

fn on_event(e &gg.Event, mut g Game) {
	if e.typ == .key_down {
		match e.key_code {
			.escape { g.ctx.quit() }
			.r { g.restart() }
			else {}
		}
		return
	}
	if e.typ != .mouse_down {
		return
	}
	x, y := int(e.mouse_x / csize), int((e.mouse_y - header_size) / csize)
	if e.mouse_button == .left && g.card2_idx == none {
		if g.remaining == 0 {
			g.restart()
			return
		}
		i := y * g.size + x
		if !g.cells[i].is_open {
			g.cells[i].is_open = true
			if g.card1_idx == none {
				g.card1_idx = i
			} else {
				g.card2_idx = i
				if g.cells[g.card1_idx].letter == g.cells[i].letter {
					g.card1_idx = none
					g.card2_idx = none
				} else {
					// start a timer, that will be checked in the on_frame callback
					g.revert_sw.start()
				}
			}
		}
	}
	g.remaining = g.cells.count(!it.is_open)
	if g.remaining == 0 {
		g.sw.stop()
	}
}

mut g := &Game{
	// the CLI argument should be number of pairs, so `size` is even, and the puzzle can be solved:
	size: arguments()[1] or { '3' }.int() * 2
}
g.restart()
g.ctx = gg.new_context(
	bg_color:     gg.black
	width:        g.size * csize
	height:       header_size + g.size * csize
	window_title: 'V Memory ${g.size} x ${g.size}'
	user_data:    g
	frame_fn:     on_frame
	event_fn:     on_event
	sample_count: 2
)
g.ctx.run()
