Message_Kind :: enum {
	Log;
	Warning;
	Error;
	Internal_Error;
}

Compiler_Message :: struct {
	message_kind : Message_Kind;
	
	message : string;
	path    : string;
	source_locations : [..]Source_Range;

	report_source_location : bool = true;
}

indent :: (builder : *String_Builder, indentation : int) {
	for 1..indentation append(builder, "    ");
}

newline :: (builder : *String_Builder) {
	append(builder, "\n");
}

green :: (builder : *String_Builder) {
	append(builder, green());
}

green :: () -> string {
	return "\x1b[92m";
}

red :: (builder : *String_Builder) {
	append(builder, red());
}

red :: () -> string {
	return "\x1b[91m";
}

yellow :: (builder : *String_Builder) {
	append(builder, yellow());
}

yellow :: () -> string {
	return "\x1b[93m";
}

cyan :: (builder : *String_Builder) {
	append(builder, cyan());
}

cyan :: () -> string {
	return "\x1b[96m";
}

white :: (builder : *String_Builder) {
	append(builder, white());
}

white :: () -> string {
	return "\x1b[97m";
}

reset_color :: () -> string {
	return "\x1b[0m";
}

reset_color :: (builder : *String_Builder) {
	append(builder, reset_color());
}

add_message :: (messages : *[..]Compiler_Message, text : string, path : string, kind : Message_Kind) {
	message : Compiler_Message;
	message.message = text;
	message.path    = path;
	message.report_source_location = false;
	message.message_kind = kind;
	array_add(messages, message);
}

log_message :: (messages : *[..]Compiler_Message, text : string, path : string) {
	add_message(messages, text, path, .Log);
}

warning_message :: (messages : *[..]Compiler_Message, text : string, path : string) {
	add_message(messages, text, path, .Warning);
}

error_message :: (messages : *[..]Compiler_Message, text : string, path : string) {
	add_message(messages, text, path, .Error);
}

internal_error_message :: (messages : *[..]Compiler_Message, text : string, path : string) {
	add_message(messages, text, path, .Internal_Error);
}

copy_messages :: (source : []Compiler_Message, dest : *[..]Compiler_Message) {
	for message : source {
		array_add(dest, message);
	}
}

report_messages :: (ctx : *Compiler_Context, messages : []Compiler_Message) -> string {
	builder : String_Builder;
	init_string_builder(*builder);
	for message : messages {
		report_message(ctx, *builder, message);
	}
	return builder_to_string(*builder);
}

report_message :: (ctx : *Compiler_Context, builder : *String_Builder, message : Compiler_Message) {
	report_message(ctx, builder, message.path, message.message, message.source_locations, message.message_kind, message.report_source_location);
}

report_message :: (ctx : *Compiler_Context, builder : *String_Builder, path : string, message : string, source_locations : []Source_Range, kind : Message_Kind, report_source_location : bool = false) {
	append(builder, "\x1b[1;37m");
	if path.count > 0 {
		print_to_builder(builder, "%:", path);
	} else {
		append(builder, "internal:");
	}

	if source_locations.count > 0 {
		print_to_builder(builder, "%,%: ", source_locations[0].main_token.line, source_locations[0].main_token.column);
	}

	if kind == .Log {
		append(builder, "\x1b[31mlog: ");
	} else if kind == .Error {
		append(builder, "\x1b[31merror: ");
	}

	append(builder, "\x1b[37m");
	print_to_builder(builder, "%\n", message);
	append(builder, "\x1b[36m");

	if report_source_location {
		for location : source_locations {
			append(builder, "\t");
			print_from_source_location(ctx, builder, location);
			append(builder, "\n\t");
			begin := location.begin;

			print_token_pointer(builder, location.main_token);
			append(builder, "\n");
		}
	}
	append(builder, "\x1b[37m");
}

