// Copyright (c) 2019-2024 Alexander Medvednikov. All rights reserved.
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.
@[has_globals]
module util

import time

__global g_timers = new_timers(should_print: false, label: 'g_timers')

@[heap]
pub struct Timers {
	label string
pub mut:
	swatches     map[string]time.StopWatch
	should_print bool
	// already_shown records for which of the swatches .show() or .show_if_exists() had been called already
	already_shown []string
}

@[params]
pub struct TimerParams {
	should_print bool
	label        string
}

pub fn new_timers(params TimerParams) &Timers {
	$if trace_timers_creation ? {
		eprintln('>>>> new_timers, should_print: ${params.should_print} | label: ${params.label}')
	}
	return &Timers{
		label: params.label
		swatches: map[string]time.StopWatch{}
		should_print: params.should_print
		already_shown: []string{cap: 100}
	}
}

pub fn get_timers() &Timers {
	return g_timers
}

pub fn timing_start(label string) {
	mut t := get_timers()
	t.start(label)
}

pub fn timing_measure(label string) {
	g_timers.show(label)
}

pub fn timing_measure_cumulative(label string) {
	g_timers.measure_cumulative(label)
}

pub fn timing_set_should_print(should_print bool) {
	g_timers.should_print = should_print
}

pub fn (mut t Timers) start(name string) {
	mut sw := t.swatches[name] or { time.new_stopwatch() }
	sw.start()
	t.swatches[name] = sw
}

pub fn (mut t Timers) measure(name string) i64 {
	if name !in t.swatches {
		timer_keys := t.swatches.keys()
		eprintln('> Timer `${name}` was NOT started.')
		eprintln('>   Available timers:')
		eprintln('>   ${timer_keys}')
	}
	ms := t.swatches[name].elapsed().microseconds()
	return ms
}

pub fn (mut t Timers) measure_cumulative(name string) i64 {
	ms := t.measure(name)
	if name !in t.swatches {
		return ms
	}
	mut sw := t.swatches[name]
	sw.pause()
	t.swatches[name] = sw
	return ms
}

pub fn (mut t Timers) measure_pause(name string) {
	if name !in t.swatches {
		return
	}
	mut sw := t.swatches[name]
	sw.pause()
	t.swatches[name] = sw
}

pub fn (mut t Timers) measure_resume(name string) {
	if name !in t.swatches {
		return
	}
	mut sw := t.swatches[name]
	sw.start()
	t.swatches[name] = sw
}

pub fn (mut t Timers) message(name string) string {
	ms := f64(t.measure(name)) / 1000.0
	value := bold('${ms:-8.3f}')
	formatted_message := '${value} ms ${name}'
	return formatted_message
}

pub fn (mut t Timers) show(label string) {
	formatted_message := t.message(label)
	if t.should_print {
		println(formatted_message)
	}
	t.already_shown << label
}

pub fn (mut t Timers) show_if_exists(label string) {
	if label !in t.swatches {
		return
	}
	t.show(label)
	t.already_shown << label
}

pub fn (mut t Timers) show_remaining() {
	for k, _ in t.swatches {
		if k in t.already_shown {
			continue
		}
		t.show(k)
	}
}

pub fn (mut t Timers) dump_all() {
	for k, _ in t.swatches {
		elapsed := t.message(k)
		println(elapsed)
	}
}
