extends Node
## A logger to log data to relevant locations
##
## A logger that logs various forms of data at different log levels to both
## built in locations in Godot as well as other components that listen to the
## provided log created signal.

signal log_created(message: String, level: LogLevel)

enum LogLevel {
	DEBUG = 0,
	INFO = 1,
	WARN = 2,
	ERROR = 3
}

## The log level that should be outputted as a minimum.
@export var log_level: LogLevel


## Log a message at log level debug (meant for troubleshooting).
func debug(message: String, arguments: Dictionary = {}) -> void:
	if log_level > LogLevel.DEBUG:
		return
	
	_log(message, LogLevel.DEBUG, arguments)


## Log a message at log level info (log to indicate something happened).
func info(message: String, arguments: Dictionary = {}) -> void:
	if log_level > LogLevel.INFO:
		return
	
	_log(message, LogLevel.INFO, arguments)


## Log a warning at log level warning (something unexpected happened but it can
## continue).
func warn(message: String, arguments: Dictionary = {}) -> void:
	_log(message, LogLevel.WARN, arguments)


## Log an error at log level error (an issue that prevents something from
## functioning).
func error(message: String, arguments: Dictionary = {}) -> void:
	_log(message, LogLevel.ERROR, arguments)


func _log(message: String, level: LogLevel, arguments: Dictionary = {}) -> void:
	var category = arguments.category if arguments.has("category") and arguments.category else "???"
	var color = arguments.color if arguments.has("color") else "olive"
	var image = arguments.image if arguments.has("image") and arguments.image else "res://components/Logger/scroll-text.svg"
	
	var adjusted_message = _clean_message(message)
	
	var constructed_message = "[color=%s][%s][/color] [img= width=12 height=12 valign=center]%s[/img] %s" % [color, category, image, adjusted_message]
	print_rich(constructed_message)
	log_created.emit(constructed_message, level)


func _clean_message(message: String) -> String:
	var cleans = [
		{
			"type": "button",
			"regex": "μ(.*)μ",
			"color": Color.html("#a4bf37")
		},
		{
			"type": "key",
			"regex": "<(.*)>",
			"color": Color.html("#42ad24")
		},
		{
			"type": "tool",
			"regex": "λ(.*)λ",
			"color": Color.html("#bf9d37")
		},
		{
			"type": "object",
			"regex": "→(.*)←",
			"color": Color.html("#854322")
		},
		{
			"type": "path",
			"regex": "♢(.*)♢",
			"color": Color.html("#22852e")
		},
		{
			"type": "function",
			"regex": "∨(.*)∨",
			"color": Color.html("#ad2452")
		},
		{
			"type": "trigger",
			"regex": "∧(.*)∧",
			"color": Color.html("#ad2d24")
		},
		{
			"type": "category",
			"regex": "\\{(.*)\\}",
			"color": Color.html("#ad9b24")
		},
		{
			"type": "value",
			"regex": "\\|(.*)\\|",
			"color": Color.TEAL
		}
	]
	
	var adjusted_message = message
	
	for clean in cleans:
		adjusted_message = _replace_regex_color(adjusted_message, clean.regex, clean.color)
	
	return adjusted_message


func _replace_regex_color(message: String, regex_string: String, color: Color) -> String:
	return _replace_regex(message, regex_string, "[color=" + color.to_html() + "]%s[/color]")


func _replace_regex(message: String, regex_string: String, new_content: String) -> String:
	var adjusted_message = message
	var stripped_message = message
	
	var regex = RegEx.new()
	regex.compile(regex_string)
	var result = regex.search(adjusted_message)
	
	while result:
		var before_content = adjusted_message.substr(0, result.get_start())
		var after_content = adjusted_message.substr(result.get_end(), adjusted_message.length())
		var template_string = "%s" + new_content + "%s"
		adjusted_message = template_string % [before_content, result.get_string(1), after_content]
		
		result = regex.search(adjusted_message)
	
	return adjusted_message