#include <inc/framebuffer.hpp>
#include <inc/memory.hpp>
#include <inc/syslog.hpp>
#include <inc/vga_buffer.hpp>

static const uint8_t font[128 * 8] = {
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x7e, 0x81, 0xa5, 0x81, 0xbd, 0x99, 0x81, 0x7e,
	0x7e, 0xff, 0xdb, 0xff, 0xc3, 0xe7, 0xff, 0x7e,
	0x6c, 0xfe, 0xfe, 0xfe, 0x7c, 0x38, 0x10, 0x00,
	0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x10, 0x00,
	0x38, 0x7c, 0x38, 0xfe, 0xfe, 0x7c, 0x38, 0x7c,
	0x10, 0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x7c,
	0x00, 0x00, 0x18, 0x3c, 0x3c, 0x18, 0x00, 0x00,
	0xff, 0xff, 0xe7, 0xc3, 0xc3, 0xe7, 0xff, 0xff,
	0x00, 0x3c, 0x66, 0x42, 0x42, 0x66, 0x3c, 0x00,
	0xff, 0xc3, 0x99, 0xbd, 0xbd, 0x99, 0xc3, 0xff,
	0x0f, 0x07, 0x0f, 0x7d, 0xcc, 0xcc, 0xcc, 0x78,
	0x3c, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x18,
	0x3f, 0x33, 0x3f, 0x30, 0x30, 0x70, 0xf0, 0xe0,
	0x7f, 0x63, 0x7f, 0x63, 0x63, 0x67, 0xe6, 0xc0,
	0x99, 0x5a, 0x3c, 0xe7, 0xe7, 0x3c, 0x5a, 0x99,
	0x80, 0xe0, 0xf8, 0xfe, 0xf8, 0xe0, 0x80, 0x00,
	0x02, 0x0e, 0x3e, 0xfe, 0x3e, 0x0e, 0x02, 0x00,
	0x18, 0x3c, 0x7e, 0x18, 0x18, 0x7e, 0x3c, 0x18,
	0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x66, 0x00,
	0x7f, 0xdb, 0xdb, 0x7b, 0x1b, 0x1b, 0x1b, 0x00,
	0x3e, 0x63, 0x38, 0x6c, 0x6c, 0x38, 0xcc, 0x78,
	0x00, 0x00, 0x00, 0x00, 0x7e, 0x7e, 0x7e, 0x00,
	0x18, 0x3c, 0x7e, 0x18, 0x7e, 0x3c, 0x18, 0xff,
	0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x00,
	0x18, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x00,
	0x00, 0x18, 0x0c, 0xfe, 0x0c, 0x18, 0x00, 0x00,
	0x00, 0x30, 0x60, 0xfe, 0x60, 0x30, 0x00, 0x00,
	0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xfe, 0x00, 0x00,
	0x00, 0x24, 0x66, 0xff, 0x66, 0x24, 0x00, 0x00,
	0x00, 0x18, 0x3c, 0x7e, 0xff, 0xff, 0x00, 0x00,
	0x00, 0xff, 0xff, 0x7e, 0x3c, 0x18, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x30, 0x78, 0x78, 0x30, 0x30, 0x00, 0x30, 0x00,
	0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x6c, 0x6c, 0xfe, 0x6c, 0xfe, 0x6c, 0x6c, 0x00,
	0x30, 0x7c, 0xc0, 0x78, 0x0c, 0xf8, 0x30, 0x00,
	0x00, 0xc6, 0xcc, 0x18, 0x30, 0x66, 0xc6, 0x00,
	0x38, 0x6c, 0x38, 0x76, 0xdc, 0xcc, 0x76, 0x00,
	0x60, 0x60, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x18, 0x30, 0x60, 0x60, 0x60, 0x30, 0x18, 0x00,
	0x60, 0x30, 0x18, 0x18, 0x18, 0x30, 0x60, 0x00,
	0x00, 0x66, 0x3c, 0xff, 0x3c, 0x66, 0x00, 0x00,
	0x00, 0x30, 0x30, 0xfc, 0x30, 0x30, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x60,
	0x00, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x00,
	0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x80, 0x00,
	0x7c, 0xc6, 0xce, 0xde, 0xf6, 0xe6, 0x7c, 0x00,
	0x30, 0x70, 0x30, 0x30, 0x30, 0x30, 0xfc, 0x00,
	0x78, 0xcc, 0x0c, 0x38, 0x60, 0xcc, 0xfc, 0x00,
	0x78, 0xcc, 0x0c, 0x38, 0x0c, 0xcc, 0x78, 0x00,
	0x1c, 0x3c, 0x6c, 0xcc, 0xfe, 0x0c, 0x1e, 0x00,
	0xfc, 0xc0, 0xf8, 0x0c, 0x0c, 0xcc, 0x78, 0x00,
	0x38, 0x60, 0xc0, 0xf8, 0xcc, 0xcc, 0x78, 0x00,
	0xfc, 0xcc, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x00,
	0x78, 0xcc, 0xcc, 0x78, 0xcc, 0xcc, 0x78, 0x00,
	0x78, 0xcc, 0xcc, 0x7c, 0x0c, 0x18, 0x70, 0x00,
	0x00, 0x30, 0x30, 0x00, 0x00, 0x30, 0x30, 0x00,
	0x00, 0x30, 0x30, 0x00, 0x00, 0x30, 0x30, 0x60,
	0x18, 0x30, 0x60, 0xc0, 0x60, 0x30, 0x18, 0x00,
	0x00, 0x00, 0xfc, 0x00, 0x00, 0xfc, 0x00, 0x00,
	0x60, 0x30, 0x18, 0x0c, 0x18, 0x30, 0x60, 0x00,
	0x78, 0xcc, 0x0c, 0x18, 0x30, 0x00, 0x30, 0x00,
	0x7c, 0xc6, 0xde, 0xde, 0xde, 0xc0, 0x78, 0x00,
	0x30, 0x78, 0xcc, 0xcc, 0xfc, 0xcc, 0xcc, 0x00,
	0xfc, 0x66, 0x66, 0x7c, 0x66, 0x66, 0xfc, 0x00,
	0x3c, 0x66, 0xc0, 0xc0, 0xc0, 0x66, 0x3c, 0x00,
	0xf8, 0x6c, 0x66, 0x66, 0x66, 0x6c, 0xf8, 0x00,
	0xfe, 0x62, 0x68, 0x78, 0x68, 0x62, 0xfe, 0x00,
	0xfe, 0x62, 0x68, 0x78, 0x68, 0x60, 0xf0, 0x00,
	0x3c, 0x66, 0xc0, 0xc0, 0xce, 0x66, 0x3e, 0x00,
	0xcc, 0xcc, 0xcc, 0xfc, 0xcc, 0xcc, 0xcc, 0x00,
	0x78, 0x30, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00,
	0x1e, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0x78, 0x00,
	0xe6, 0x66, 0x6c, 0x78, 0x6c, 0x66, 0xe6, 0x00,
	0xf0, 0x60, 0x60, 0x60, 0x62, 0x66, 0xfe, 0x00,
	0xc6, 0xee, 0xfe, 0xfe, 0xd6, 0xc6, 0xc6, 0x00,
	0xc6, 0xe6, 0xf6, 0xde, 0xce, 0xc6, 0xc6, 0x00,
	0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x00,
	0xfc, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xf0, 0x00,
	0x78, 0xcc, 0xcc, 0xcc, 0xdc, 0x78, 0x1c, 0x00,
	0xfc, 0x66, 0x66, 0x7c, 0x6c, 0x66, 0xe6, 0x00,
	0x78, 0xcc, 0xe0, 0x70, 0x1c, 0xcc, 0x78, 0x00,
	0xfc, 0xb4, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00,
	0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xfc, 0x00,
	0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x00,
	0xc6, 0xc6, 0xc6, 0xd6, 0xfe, 0xee, 0xc6, 0x00,
	0xc6, 0xc6, 0x6c, 0x38, 0x38, 0x6c, 0xc6, 0x00,
	0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x30, 0x78, 0x00,
	0xfe, 0xc6, 0x8c, 0x18, 0x32, 0x66, 0xfe, 0x00,
	0x78, 0x60, 0x60, 0x60, 0x60, 0x60, 0x78, 0x00,
	0xc0, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x02, 0x00,
	0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x78, 0x00,
	0x10, 0x38, 0x6c, 0xc6, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
	0x30, 0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0x76, 0x00,
	0xe0, 0x60, 0x60, 0x7c, 0x66, 0x66, 0xdc, 0x00,
	0x00, 0x00, 0x78, 0xcc, 0xc0, 0xcc, 0x78, 0x00,
	0x1c, 0x0c, 0x0c, 0x7c, 0xcc, 0xcc, 0x76, 0x00,
	0x00, 0x00, 0x78, 0xcc, 0xfc, 0xc0, 0x78, 0x00,
	0x38, 0x6c, 0x60, 0xf0, 0x60, 0x60, 0xf0, 0x00,
	0x00, 0x00, 0x76, 0xcc, 0xcc, 0x7c, 0x0c, 0xf8,
	0xe0, 0x60, 0x6c, 0x76, 0x66, 0x66, 0xe6, 0x00,
	0x30, 0x00, 0x70, 0x30, 0x30, 0x30, 0x78, 0x00,
	0x0c, 0x00, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0x78,
	0xe0, 0x60, 0x66, 0x6c, 0x78, 0x6c, 0xe6, 0x00,
	0x70, 0x30, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00,
	0x00, 0x00, 0xcc, 0xfe, 0xfe, 0xd6, 0xc6, 0x00,
	0x00, 0x00, 0xf8, 0xcc, 0xcc, 0xcc, 0xcc, 0x00,
	0x00, 0x00, 0x78, 0xcc, 0xcc, 0xcc, 0x78, 0x00,
	0x00, 0x00, 0xdc, 0x66, 0x66, 0x7c, 0x60, 0xf0,
	0x00, 0x00, 0x76, 0xcc, 0xcc, 0x7c, 0x0c, 0x1e,
	0x00, 0x00, 0xdc, 0x76, 0x66, 0x60, 0xf0, 0x00,
	0x00, 0x00, 0x7c, 0xc0, 0x78, 0x0c, 0xf8, 0x00,
	0x10, 0x30, 0x7c, 0x30, 0x30, 0x34, 0x18, 0x00,
	0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00,
	0x00, 0x00, 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x00,
	0x00, 0x00, 0xc6, 0xd6, 0xfe, 0xfe, 0x6c, 0x00,
	0x00, 0x00, 0xc6, 0x6c, 0x38, 0x6c, 0xc6, 0x00,
	0x00, 0x00, 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0xf8,
	0x00, 0x00, 0xfc, 0x98, 0x30, 0x64, 0xfc, 0x00,
	0x1c, 0x30, 0x30, 0xe0, 0x30, 0x30, 0x1c, 0x00,
	0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x00,
	0xe0, 0x30, 0x30, 0x1c, 0x30, 0x30, 0xe0, 0x00,
	0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0x00,
};

static const uint32_t color_to_rgb[16] = {
	0x000000, 0x000080, 0x008000, 0x008080,
	0x800000, 0x800080, 0x808000, 0xC0C0C0,
	0x808080, 0x0000FF, 0x00FF00, 0x00FFFF,
	0xFF0000, 0xFF00FF, 0xFFFF00, 0xFFFFFF,
};

namespace FrameBuffer {
	static volatile uint32_t *framebuffer;
	static bool initialized;
	
	struct Char {
		uint8_t byte;
		VGA_Buffer::ColorCode color_code;
	};
	
	const int N_ROWS = 600 / 12;
	const int N_COLS = 800 / 8;
	static Char chars[N_ROWS][N_COLS];
	
	static int current_col;
	static VGA_Buffer::ColorCode current_color_code;
	
	static void draw_char(int row, int col, Char c) {
		uint32_t byte = c.byte >= 128 ? 0 : c.byte;
		const uint8_t *glyph = font + byte * 8;
		
		uint32_t front_color = color_to_rgb[c.color_code.c >> 4];
		uint32_t back_color = color_to_rgb[c.color_code.c & 0xf];
		
		int pos_row = row * 12, pos_col = col * 8;
		for (int i = 0; i < 12; i++) {
			uint8_t line = (i >= 2 && i < 10) ? glyph[i - 2] : 0;
			for (int j = 0; j < 8; j++) {
				uint8_t bit = (line >> (7 - j)) & 1;
				uint32_t color = bit ? front_color : back_color;
				framebuffer[(pos_row + i) * 800 + pos_col + j] = color;
			}
		}
	}
	
	static void draw_new_line() {
		for (int i = 0; i < N_ROWS - 1; i++) {
			for (int j = 0; j < N_COLS; j++) {
				chars[i][j] = chars[i + 1][j];
				draw_char(i, j, chars[i][j]);
			}
		}
		
		for (int j = 0; j < N_COLS; j++) {
			chars[N_ROWS - 1][j] = (Char) {' ', current_color_code};
			draw_char(N_ROWS - 1, j, chars[N_ROWS - 1][j]);
		}
		
		current_col = 0;
	}
	
	void init() {
		if (!framebuffer) return;
		if (framebuffer == Memory::remap((volatile uint32_t *) 0)) return;
		initialized = true;
		
		draw_new_line();
		
		// Replay the syslog
		char buf[32];
		int offset = 0;
		auto colorcode = VGA_Buffer::writer->color_code;
		
		while (1) {
			int len = SysLog::get(offset, 32, buf);
			if (len == 0) break;
			for (int i = 0; i < len; i++) {
				write_byte(buf[i], colorcode);
			}
			offset += len;
		}
	}
	
	void set_framebuffer_addr(uint64_t addr) {
		framebuffer = Memory::remap((volatile uint32_t *) addr);
	}
	
	void write_byte(uint8_t byte, VGA_Buffer::ColorCode color_code) {
		if (!initialized) return;
		
		if (byte == '\n') {
			current_color_code = color_code;
			draw_new_line();
		} else {
			if (current_col >= N_COLS) {
				draw_new_line();
			}
			
			Char c = (Char) {byte, color_code};
			draw_char(N_ROWS - 1, current_col, c);
			chars[N_ROWS - 1][current_col] = c;
			current_col++;
			current_color_code = color_code;
		}
	}
}
