package main

import "core:fmt"
import "core:os"
import "core:time"

main :: proc() {
	fmt.println("Speedcat bootstrap compiler")
	fmt.println("===========================")

	handle: os.Handle
	options := parse_args(&os.args)
	file_name := options.file
	if file_name != "<stdin>" {
		errno: os.Errno
		handle, errno = os.open(file_name)
		if errno != 0 {
			fmt.printf("Error opening file\n", errno)
			return
		}
	} else {
		handle = os.stdin
	}
	defer os.close(handle)

	data, err := os.read_entire_file_from_handle(handle)
	if !err {
		fmt.printf("Error reading file\n", err)
		return
	}

	u8_arr: [dynamic]u8
	for ch in data {
		append(&u8_arr, u8(ch))
	}

	lexer := lexer_create(&u8_arr, file_name)
	parser := parser_create(lexer)

	parser_time_start := time.now()
	ast := parser_parse(&parser)
	parser_duration := time.since(parser_time_start)

	if len(g_message_list) > 0 {
		contains_errors := false
		for &msg in g_message_list {
			message_print(&msg, &data)
			if msg.level == .Error || msg.level == .Fatal {
				contains_errors = true
			}
		}
		if contains_errors {
			os.exit(1)
		}
	}
	clear(&g_message_list)

	type_check_start := time.now()
	type_check(ast, nil)
	type_check_duration := time.since(type_check_start)

	if len(g_message_list) > 0 {
		contains_errors := false
		for &msg in g_message_list {
			message_print(&msg, &data)
			if msg.level == .Error || msg.level == .Fatal {
				contains_errors = true
			}
		}
		if contains_errors {
			os.exit(1)
		}
	}

	if options.dump_ast {
		node_print(ast)
	}

	fmt.println("Time report")
	fmt.println("===========")
	most_printed := 0
	printed := fmt.printf(
		"Parsing took:         % 10dns (%.3fms)\n",
		time.duration_nanoseconds(parser_duration),
		time.duration_milliseconds(parser_duration),
	)
	most_printed = max(printed, most_printed)
	printed = fmt.printf(
		"Type checker took:    % 10dns (%.3fms)\n",
		time.duration_nanoseconds(type_check_duration),
		time.duration_milliseconds(type_check_duration),
	)
	most_printed = max(printed, most_printed)
	llvm_duration: time.Duration = 0
	defer {
		for most_printed > 1 {
			fmt.printf("-")
			most_printed -= 1
		}
		fmt.printf(
			"\nTotal time:           % 10dns (%.3fms)\n\n",
			time.duration_nanoseconds(llvm_duration + type_check_duration + parser_duration),
			time.duration_milliseconds(llvm_duration + type_check_duration + parser_duration),
		)
	}

	if options.dont_emit_llvm {
		return
	}

	name: string
	if handle == os.stdin {
		name = "stdin"
	} else {
		name = os.args[1]
	}
	name_dyn := [dynamic]u8{}
	for ch in transmute([]u8)name {
		append(&name_dyn, u8(ch))
	}
	module_name := parse_use_path2(name_dyn)
	module_name = main_module_name_from_filename(module_name)
	if len(module_name) == 0 {
		clear(&module_name)
		append(&module_name, 'm')
		append(&module_name, 'a')
		append(&module_name, 'i')
		append(&module_name, 'n')
	}
	ctx := LLVMContextCreate()
	defer LLVMContextDispose(ctx)
	module := LLVMModuleCreateWithNameInContext(cstring(raw_data(module_name[:])), ctx)
	defer LLVMDisposeModule(module)
	builder := LLVMCreateBuilderInContext(ctx)

	llvm_start := time.now()
	generate_llvm(ctx, module, builder, ast)
	llvm_duration = time.since(llvm_start)
	printed = fmt.printf(
		"LLVM generation took: % 10dns (%.3fms)\n",
		time.duration_nanoseconds(llvm_duration),
		time.duration_milliseconds(llvm_duration),
	)

	append(&module_name, '.')
	append(&module_name, 'l')
	append(&module_name, 'l')
	LLVMPrintModuleToFile(module, cstring(raw_data(module_name[:])), nil)
}

main_module_name_from_filename :: proc(fname: [dynamic]u8) -> (module_name: [dynamic]u8) {
	temp_name := [dynamic]u8{}
	for ch in fname {
		if ch == '/' || ch == '\\' {
			clear(&temp_name)
		} else {
			append(&temp_name, ch)
		}
	}

	for ch in temp_name {
		if ch == '.' {
			break
		}
		append(&module_name, ch)
	}

	return
}
