module sim

import benchmark
import term

pub type SimRequestHandler = fn (request &SimRequest) !

pub type SimStartHandler = fn () !

pub type SimFinishHandler = fn () !

pub const default_width = 600
pub const default_height = 600

@[params]
pub struct GridSettings {
pub:
	width  int = sim.default_width
	height int = sim.default_height
}

pub fn new_grid_settings(settings GridSettings) GridSettings {
	return GridSettings{
		...settings
	}
}

@[params]
pub struct RunnerSettings {
pub:
	grid       GridSettings
	on_request SimRequestHandler = unsafe { nil }
	on_start   SimStartHandler   = unsafe { nil }
	on_finish  SimFinishHandler  = unsafe { nil }
}

pub fn run(params SimParams, settings RunnerSettings) {
	height := settings.grid.height
	width := settings.grid.width

	if !isnil(settings.on_start) {
		settings.on_start() or {
			log(@MOD + '.' + @FN + ': Simulation start handler failed. Error ${err}')
		}
	}

	mut index := 0
	log('')

	mut bmark := benchmark.new_benchmark()
	for y in 0 .. height {
		$if verbose ? {
			term.clear_previous_line()
		}
		log(@MOD + '.' + @FN + ': y: ${y + 1}')
		for x in 0 .. width {
			bmark.step()
			// setup state conditions
			position := vector(
				x: 0.1 * ((f64(x) - 0.5 * f64(width - 1)) / f64(width - 1))
				y: 0.1 * ((f64(y) - 0.5 * f64(height - 1)) / f64(height - 1))
				z: 0.0
			)
			velocity := vector(x: 0, y: 0, z: 0)

			mut state := new_state(
				position: position
				velocity: velocity
			)

			state.satisfy_rope_constraint(params)
			request := &SimRequest{
				id: index
				state: state
				params: params
			}
			settings.on_request(request) or {
				log(@MOD + '.' + @FN + ': request handler failed. Error ${err}')
				bmark.fail()
				break
			}
			index++
			bmark.ok()
		}
	}
	bmark.stop()
	println(bmark.total_message(@FN))

	if !isnil(settings.on_finish) {
		settings.on_finish() or {
			log(@MOD + '.' + @FN + ': Simulation stop handler failed. Error ${err}')
		}
	}
}
