#include "console.h"
#include "string.h"
#include "sprintf.h"
#include "spinlock.h"
#include "fileobj.h"

int tty_write(struct file_object *fo, void *buf, size_t n);

struct fops tty_fops = {
	.open = &fops_dummy_open,
	.write = &tty_write,
	.close = &fops_dummy_open,
};

struct file_object tty_fo;

spinlock_t console_lock = 0;

char *VGA = (char *)0x0B8000;

int console_x = 0, console_y = 0;

int tty_write(struct file_object *fo, void *buf, size_t n) {
	int i;
	for (i = 0; i < n; i++) {
		putchar(((char *)buf)[i]);
	}
	return n;
}

void console_init() {
	fileobj_init(&tty_fo);
	tty_fo.fops = &tty_fops;

	devfs_register_device("tty", &tty_fo);
}

void scroll_up(int rows) {
	if (rows <= 0) {
		return;
	}
	int moveby = rows * CONSOLE_COLS * 2;
	int n = (CONSOLE_ROWS * CONSOLE_COLS * 2) - moveby;
	memcpy(VGA, VGA + moveby, n);

	char *p, *ep;
	ep = VGA + CONSOLE_ROWS * CONSOLE_COLS * 2;
	for (p = VGA + n; p < ep; p += 2) {
		*p = ' ';
		*(p + 1) = 0x07;
	}
}

void putchar(char c) {
	if (c == '\n') {
		console_y++;
		console_x = 0;
	} else {
		VGA[CONSOLE_COLS * console_y * 2 + console_x * 2] = c;
		console_x++;
		if (console_x >= CONSOLE_COLS) {
			console_y++;
			console_x = 0;
		}
	}

	if (console_y >= CONSOLE_ROWS) {
//		console_y = 0;
		scroll_up(console_y - CONSOLE_ROWS + 1);
		console_y = CONSOLE_ROWS - 1;
		console_x = 0;
	}
}

void puts(char *string) {
	for (; *string; string++) {
		putchar(*string);
	}
}

void clrscr() {
	char *p, *ep = VGA + 25 * 80 * 2;

	for (p = VGA; p < ep; p += 2) {
		*p = ' ';
		*(p + 1) = 0x07;
	}

	console_x = console_y = 0;
}

// Yes, this is really krep...
void printf(const char *format, ...) {
	static char buffer[1024];
	va_list argp;

	va_start(argp, format);

	vsprintf(buffer, format, argp);
	puts(buffer);
	
	va_end(argp);
}
