/* vim: tabstop=4 shiftwidth=4 noexpandtab
 * This file is part of ToaruOS and is released under the terms
 * of the NCSA / University of Illinois License - see LICENSE.md
 * Copyright (C) 2011-2014 Kevin Lange
 *
 * Panic functions
 */
#include <system.h>
#include <logging.h>
#include <printf.h>
#include <module.h>

void halt_and_catch_fire(char * error_message, const char * file, int line, struct regs * regs) {
	IRQ_OFF;
	debug_print(ERROR, "HACF: %s", error_message);
	debug_print(ERROR, "Proc: %d", getpid());
	debug_print(ERROR, "File: %s", file);
	debug_print(ERROR, "Line: %d", line);
	if (regs) {
		debug_print(ERROR, "Registers at interrupt:");
		debug_print(ERROR, "eax=0x%x ebx=0x%x", regs->eax, regs->ebx);
		debug_print(ERROR, "ecx=0x%x edx=0x%x", regs->ecx, regs->edx);
		debug_print(ERROR, "esp=0x%x ebp=0x%x", regs->esp, regs->ebp);
		debug_print(ERROR, "Error code: 0x%x",  regs->err_code);
		debug_print(ERROR, "EFLAGS:     0x%x",  regs->eflags);
		debug_print(ERROR, "User ESP:   0x%x",  regs->useresp);
		debug_print(ERROR, "eip=0x%x",          regs->eip);
	}
	debug_print(ERROR, "This process has been descheduled.");
	kexit(1);
}

char * probable_function_name(uintptr_t ip, uintptr_t * out_addr) {
	char * closest  = NULL;
	size_t distance = 0xFFFFFFFF;
	uintptr_t  addr = 0;

	if (modules_get_symbols()) {
		list_t * hash_keys = hashmap_keys(modules_get_symbols());
		foreach(_key, hash_keys) {
			char * key = (char *)_key->value;
			uintptr_t a = (uintptr_t)hashmap_get(modules_get_symbols(), key);

			if (!a) continue;

			size_t d = 0xFFFFFFFF;
			if (a <= ip) {
				d = ip - a;
			}
			if (d < distance) {
				closest = key;
				distance = d;
				addr = a;
			}
		}
		free(hash_keys);

	}
	*out_addr = addr;
	return closest;
}

void assert_failed(const char *file, uint32_t line, const char *desc) {
	IRQ_OFF;
	debug_print(INSANE, "Kernel Assertion Failed: %s", desc);
	debug_print(INSANE, "File: %s", file);
	debug_print(INSANE, "Line: %d", line);
	debug_print(INSANE, "System Halted!");

#if 1
	unsigned int * ebp = (unsigned int *)(&file - 2);

	debug_print(INSANE, "Stack trace:");

	for (unsigned int frame = 0; frame < 20; ++frame) {
		unsigned int eip = ebp[1];
		if (eip == 0) break;
		ebp = (unsigned int *)(ebp[0]);
		unsigned int * args = &ebp[2];
		(void)args;
		uintptr_t addr;
		char * func = probable_function_name(eip, &addr);
		debug_print(INSANE, "    0x%x (%s+%d)\n", eip, func, eip-addr);
	}


#endif

	if (debug_video_crash) {
		char msg[4][256];
		sprintf(msg[0], "Kernel Assertion Failed: %s", desc);
		sprintf(msg[1], "File: %s", file);
		sprintf(msg[2], "Line: %d", line);
		sprintf(msg[3], "System Halted!");
		char * msgs[] = {msg[0], msg[1], msg[2], msg[3], NULL};
		debug_video_crash(msgs);
	}

	while (1) {
		IRQ_OFF;
		PAUSE;
	}
}
