@icon("stopwatch.svg")
class_name Stopwatch
extends Node


## A clock that counts upwards, like a stopwatch.

## Emitted, whenever the elapsed time reaches the [param max_time] limit.
signal max_time_reached

## See documentation for [param process_callback] in [Timer]
@export var process_callback: Timer.TimerProcessCallback
## The time, where we count upwards from when [method start] is called.
@export var start_time: float = 0.0
## The maximum allowed elapsed time we can have.[br]If this is under [code]0[/code]
## we don't have any limit and we count towards infinity.
@export var max_time: float = -1.0
## If [code]true[/code], the stopwatch will call [method start] when entering the scene tree.
@export var autostart: bool = false
## If [code]true[/code], it will reset the timer when [method stop] is being called.
@export var reset_timer_on_stop: bool = true


## The stopwatch will halt it's counting when this is [code]true[/code].
var paused: bool = false
var _stopped: bool = true
var _time_elapsed: float = 0.0


func _ready() -> void:
	if autostart:
		start()


func _process(delta: float) -> void:
	if process_callback == Timer.TIMER_PROCESS_IDLE:
		_process_timer(delta)


func _physics_process(delta: float) -> void:
	if process_callback == Timer.TIMER_PROCESS_PHYSICS:
		_process_timer(delta)


func _process_timer(delta: float) -> void:
	if is_stopped() or is_paused():
		return
	
	_time_elapsed += delta
	
	if (max_time >= 0.0) and (_time_elapsed >= max_time):
		max_time_reached.emit()
		stop()


#region Starting/Stopping
## Starts the stopwatch. [param custom_start_time] will set the elapsed time to it instead of
## using [param start_time].
func start(custom_start_time: float = start_time) -> void:
	_time_elapsed = custom_start_time
	_stopped = false


## Stops the stopwatch.
func stop(reset_time: bool = reset_timer_on_stop) -> float:
	var final_time: float = _time_elapsed
	
	if reset_time:
		_time_elapsed = start_time
	
	_stopped = true
	return final_time
#endregion


## Returns the elapsed time since this stopwatch was started.
func get_elapsed_time() -> float:
	return _time_elapsed


## Will pause or unpause the counting.
func set_paused(value: bool) -> void:
	paused = value


## Returns [code]true[/code] when this stopwatch isn't running.
func is_stopped() -> bool:
	return _stopped

## Returns [code]true[/code] when this stopwatch is [u]paused[/u].
func is_paused() -> bool:
	return paused
