#include "duffle.amd64.win32.h"

// --- Semantic Tags (Using X-Macros & Enum_) ---
// Colors translated from Cozy-and-WIndy: 
// 0x00bbggrr Win32 format

#define Tag_Entries() \
		X(Define,  "Define",  0x0018AEFF, ":") /* Orange-ish (Language.Type) */ \
		X(Call,    "Call",    0x00D6A454, "~") /* Soft Blue (Language.Class) */ \
		X(Data,    "Data",    0x0094BAA1, "$") /* Muted Green (Language.Number) */ \
		X(Imm,     "Imm",     0x004AA4C2, "^") /* Sand/Yellow (Language.Keyword) */ \
		X(Comment, "Comment", 0x00AAAAAA, ".") /* Grey (Language.Comment) */ \
		X(Format,  "Format",  0x003A2F3B, " ") /* Current Line BG for invisibles */

typedef Enum_(U4, STag) {
#define X(n, s, c, p) tmpl(STag, n),
		Tag_Entries()
#undef X
		STag_Count,
};
global U4 tag_colors[] = {
#define X(n, s, c, p) c,
		Tag_Entries()
#undef X
};
global const char* tag_prefixes[] = {
#define X(n, s, c, p) p,
		Tag_Entries()
#undef X
};
global const char* tag_names[] = {
#define X(n, s, c, p) s,
		Tag_Entries()
#undef X
};

// Token Packing: 28 bits payload | 4 bits tag
#define pack_token(tag, val) ((u4_(tag) << 28) | (u4_(val) & 0x0FFFFFFF))
#define unpack_tag(token)    ( ((token) >> 28) & 0x0F)
#define unpack_val(token)    (  (token)        & 0x0FFFFFFF)

// 2-Character Mapped Dictionary Helper
#define id2(a, b) ((u4_(a) << 8) | u4_(b))

#define TOKENS_PER_ROW 8

#define MODE_NAV  0
#define MODE_EDIT 1

// The Tape Drive (Using FArena from duffle)
global FArena tape_arena;
global FArena anno_arena;
global U8     cursor_idx  = 0;
global U4     editor_mode = MODE_NAV;
global U4     mode_switch_now = false;

// Executable Code Arena (The JIT)
global FArena code_arena;

// VM State: 2-Reg Stack + Global Memory
global U8 vm_rax         = 0; // Top
global U8 vm_rdx         = 0; // Next
global U8 vm_globals[16] = {0};

// Execution Mode & Logging
global B4 run_full       = false;
global U8 log_buffer[16] = {0};
global U4 log_count      = 0;

// UI State
global S4 scroll_y_offset = 0;

void ms_builtin_print(U8 val) {
	if (log_count < 16) {
		log_buffer[log_count++] = val;
	}
}

// Dictionary
typedef struct {
	U4 val;
	U4 offset;
} DictEntry;
global DictEntry dict[256];
global U8        dict_count = 0;

IA_ void scatter(U4 token, const char* anno_str) { 
	if (tape_arena.used + sizeof(U4) <= tape_arena.capacity && anno_arena.used + sizeof(U8) <= anno_arena.capacity) {
		U4*r ptr = u4_r(tape_arena.start + tape_arena.used);
		ptr[0]           = token;
		tape_arena.used += sizeof(U4);
		U8*r aptr = u8_r(anno_arena.start + anno_arena.used);
		aptr[0] = 0;
		if (anno_str) {
			char* dest = (char*)aptr;
			int i = 0; while(i < 8 && anno_str[i]) {
				dest[i] = anno_str[i];
				i ++;
			}
		}
		anno_arena.used += sizeof(U8);
	}
}

// --- Minimal x86-64 Emitter ---
internal void emit8(U1 b) { 
	if (code_arena.used + 1 <= code_arena.capacity) {
		U1*r ptr = u1_r(code_arena.start + code_arena.used);
		ptr[0]           = b;
		code_arena.used += 1;
	}
}
internal void emit32(U4 val) { 
	if (code_arena.used + 4 <= code_arena.capacity) {
		U4*r ptr = u4_r(code_arena.start + code_arena.used);
		ptr[0]           = val;
		code_arena.used += 4;
	}
}

internal void compile_action(U4 val) 
{
	if (val == id2('S','W')) { // SWAP: xchg rax, rdx
		emit8(0x48); emit8(0x87); emit8(0xC2);
		return;
	} else if (val == id2('M','*')) { // MULT: imul rax, rdx
		emit8(0x48); emit8(0x0F); emit8(0xAF); emit8(0xC2);
		return;
	} else if (val == id2('+',' ')) { // ADD: add rax, rdx
		emit8(0x48); emit8(0x01); emit8(0xD0);
		return;
	} else if (val == id2('@',' ')) { // FETCH: mov rax, QWORD PTR [rcx + rax*8]
		emit8(0x48); emit8(0x8B); emit8(0x04); emit8(0xC1);
		return;
	} else if (val == id2('-','1')) { // DEC: dec rax
		emit8(0x48); emit8(0xFF); emit8(0xC8);
		return;
	} else if (val == id2('!',' ')) { // STORE: mov QWORD PTR [rcx + rax*8], rdx
		emit8(0x48); emit8(0x89); emit8(0x14); emit8(0xC1);
		return;
	} else if (val == id2('R','0')) { // RET_IF_ZERO: test rax, rax; jnz +1; ret
		emit8(0x48); emit8(0x85); emit8(0xC0); // test rax, rax
		emit8(0x75); emit8(0x01);              // jnz skip_ret (+1 byte)
		emit8(0xC3);                           // ret
		return;
	} else if (val == id2('R','E')) { // RET
		emit8(0xC3);
		return;
	} else if (val == id2('P','R')) { // PRINT: call ms_builtin_print
		emit8(0x51); // push rcx
		emit8(0x52); // push rdx
		emit8(0x48); emit8(0x83); emit8(0xEC); emit8(0x20); // sub rsp, 32
		emit8(0x48); emit8(0x89); emit8(0xC1); // mov rcx, rax
		emit8(0x49); emit8(0xB8); // mov r8, imm64
		U8 addr = u8_(& ms_builtin_print);
		emit32(u4_(addr &  0xFFFFFFFF));
		emit32(u4_(addr >> 32));
		emit8(0x41); emit8(0xFF); emit8(0xD0); // call r8
		emit8(0x48); emit8(0x83); emit8(0xC4); emit8(0x20); // add rsp, 32
		emit8(0x5A); // pop rdx
		emit8(0x59); // pop rcx
		return;
	}
	// Dictionary Resolver (Call User Word)
	for (U8 entry = 0; entry < dict_count; entry++) {
		if (dict[entry].val == val) {
			U4 target  = dict[entry].offset;
			U4 current = code_arena.used;
			S4 rel32   = s4_(target) - s4_(current + 5);
			emit8(0xE8); // CALL rel32
			emit32(u4_(rel32));
			return;
		}
	}
}

IA_ void compile_and_run_tape(void) 
{
	farena_reset(& code_arena); 
	dict_count = 0;
	log_count  = 0;
	
	// Prologue: Load VM state from globals[14] and [15]
	emit8(0x48); emit8(0x8B); emit8(0x41); emit8(0x70); // mov rax, [rcx+112]
	emit8(0x48); emit8(0x8B); emit8(0x51); emit8(0x78); // mov rdx, [rcx+120]

	U4*r tape_ptr       = u4_r(tape_arena.start);
	B4   in_def         = false;
	U4   def_jmp_offset = 0;
	U8   end_idx        = run_full ? (tape_arena.used / sizeof(U4)) : (cursor_idx + 1);
	for (U8 i = 0; i < end_idx; i++) 
	{
		U4 tag = unpack_tag(tape_ptr[i]);
		U4 val = unpack_val(tape_ptr[i]);
		if (tag == STag_Define) 
		{
			if (in_def == false) {
				emit8(0xE9); // JMP rel32 (Skip over definition body)
				def_jmp_offset = code_arena.used;
				emit32(0);
				in_def = true;
			}
			if (dict_count < 256) {
				dict[dict_count].val     = val;
				dict[dict_count].offset = code_arena.used;
				dict_count++;
			}
		} 
		else if (tag == STag_Call) 
		{
			compile_action(val);
			if (val == id2('R','E') && in_def) {
				// End of definition block, patch the jump
				U4 current = code_arena.used;
				u4_r(code_arena.start + def_jmp_offset)[0] = current - (def_jmp_offset + 4);
				in_def = false;
			}
		} 
		else if (tag == STag_Data) {
			emit8(0x48); emit8(0x89); emit8(0xC2); // mov rdx, rax
			emit8(0x48); emit8(0xC7); emit8(0xC0); emit32(val); // mov rax, imm32
		} 
		else if (tag == STag_Imm) 
		{
			if (in_def) {
				// If we execute something, we jump out of def block first
				U4 current = code_arena.used;
				u4_r(code_arena.start + def_jmp_offset)[0] = current - (def_jmp_offset + 4);
				in_def = false;
			}
			compile_action(val);
		}
	}

	if (in_def) {
		// If we hit cursor inside a definition, patch jump so it doesn't crash on execution
		U4 current = code_arena.used;
		u4_r(code_arena.start + def_jmp_offset)[0] = current - (def_jmp_offset + 4);
	}

	// Epilogue: Save VM state back to globals
	emit8(0x48); emit8(0x89); emit8(0x41); emit8(0x70); // mov [rcx+112], rax
	emit8(0x48); emit8(0x89); emit8(0x51); emit8(0x78); // mov [rcx+120], rdx
	emit8(0xC3); // ret

	// Cast code arena to function pointer and CALL it!
	typedef void JIT_Func(U8* globals_ptr);
	JIT_Func* func = (JIT_Func*)code_arena.start;
	func(vm_globals);
	
	// Read state for UI
	vm_rax = vm_globals[14];
	vm_rdx = vm_globals[15];
}

// --- Window Procedure ---
S8 win_proc(void* hwnd, U4 msg, U8 wparam, S8 lparam) 
{
	U8 tape_count = tape_arena.used / sizeof(U4);
	U4*r tape_ptr = C_(U4*r, tape_arena.start);
	switch (msg)  {
	case MS_WM_CHAR: {
		if (editor_mode != MODE_EDIT) return 0;
		
		U4 t   = tape_ptr[cursor_idx];
		U4 tag = unpack_tag(t);
		U4 val = unpack_val(t);
		U1 c   = u1_(wparam);
		
		// Skip control characters and the 'E' that triggered the mode
		bool should_skip = c < 32 || (c == 'e' && mode_switch_now);
		if (should_skip) { mode_switch_now = false; return 0; }

		if (tag == STag_Data) {
			// Hex input
			U4 digit = 16;
			if (c >= '0' && c <= '9') digit = c - '0';
			if (c >= 'a' && c <= 'f') digit = c - 'a' + 10;
			if (c >= 'A' && c <= 'F') digit = c - 'A' + 10;
			if (digit < 16) {
				val = ((val << 4) | digit) & 0x0FFFFFFF;
				tape_ptr[cursor_idx] = pack_token(tag, val);
			}
		} 
		else if (tag != STag_Format) {
			U8*r  anno_ptr = u8_r(anno_arena.start);
			char* anno_str = (char*) & anno_ptr[cursor_idx];
			int    len = 0;
			while (len < 8 && anno_str[len] != '\0' && anno_str[len] != ' ') len ++;
			if    (len < 8) {
				anno_str[len] = (char)c;
				for (int i = len + 1; i < 8; i++) anno_str[i] = '\0';
				// Update the 2-char token ID from the first 2 chars
				char c1 = anno_str[0] ? anno_str[0] : ' ';
				char c2 = anno_str[1] ? anno_str[1] : ' ';
				val = id2(c1, c2);
				tape_ptr[cursor_idx] = pack_token(tag, val);
			}
		}
		vm_rax = 0; vm_rdx = 0; mem_zero(u8_(vm_globals), sizeof(vm_globals));
		compile_and_run_tape();
		ms_invalidate_rect(hwnd, nullptr, true);
		return 0;
	}
	case MS_WM_KEYDOWN: {
		if (wparam == 0x45 && editor_mode == MODE_NAV) { // 'E'
			editor_mode = MODE_EDIT;
			mode_switch_now = true;
			ms_invalidate_rect(hwnd, nullptr, true);
			return 0; 
			// ~~Consume the keypress so it doesn't trigger WM_CHAR~~
			// NOTE(Ed): Still triggers WM_CHAR we need to track when we just entered edit mode and that must be consumed.
		}
		if (wparam == 0x1B && editor_mode == MODE_EDIT) { // ESC
			editor_mode = MODE_NAV;
			ms_invalidate_rect(hwnd, nullptr, true);
			return 0;
		}
		if (editor_mode == MODE_EDIT) {
			if (wparam == MS_VK_BACK) {
				U4 t   = tape_ptr[cursor_idx];
				U4 tag = unpack_tag(t);
				U4 val = unpack_val(t);
				if (tag == STag_Data) {
					val                  = val >> 4;
					tape_ptr[cursor_idx] = pack_token(tag, val);
				} 
				else if (tag != STag_Format) {
					U8*r  anno_ptr = u8_r(anno_arena.start);
					char* anno_str = (char*) & anno_ptr[cursor_idx];
					int    len = 0;
					while (len < 8 && anno_str[len] != '\0' && anno_str[len] != ' ') len ++;
					if    (len > 0) {
						anno_str[len - 1] = '\0';
						char c1 = anno_str[0] ? anno_str[0] : ' ';
						char c2 = anno_str[1] ? anno_str[1] : ' ';
						tape_ptr[cursor_idx] = pack_token(tag, id2(c1, c2));
					}
				}
				vm_rax = 0; vm_rdx = 0; mem_zero(u8_(vm_globals), sizeof(vm_globals));
				compile_and_run_tape();
				ms_invalidate_rect(hwnd, nullptr, true);
			}
			return 0; // Block navigation keys in Edit Mode
		}
		
		if (wparam == MS_VK_RIGHT && cursor_idx < tape_count - 1) cursor_idx ++;
		if (wparam == MS_VK_LEFT  && cursor_idx > 0)              cursor_idx --;
		
		if (wparam == MS_VK_UP) {
			U8     line_start = cursor_idx;
			while (line_start > 0 && unpack_tag(tape_ptr[line_start - 1]) != STag_Format) line_start--;
			if    (line_start > 0) {
				U8 col             = cursor_idx - line_start;
				U8 prev_line_start = line_start - 1;
				while (prev_line_start > 0 && unpack_tag(tape_ptr[prev_line_start - 1]) != STag_Format) prev_line_start--;
				U8 prev_line_len = (line_start - 1) - prev_line_start;
				cursor_idx = prev_line_start + (col < prev_line_len ? col : prev_line_len);
			}
		}
		if (wparam == MS_VK_DOWN) {
			U8 line_start = cursor_idx;
			while (line_start > 0 && unpack_tag(tape_ptr[line_start - 1]) != STag_Format) line_start --;
			U8 col = cursor_idx - line_start;
			U8     next_line_start = cursor_idx;
			while (next_line_start < tape_count && unpack_tag(tape_ptr[next_line_start]) != STag_Format) next_line_start ++;
			if    (next_line_start < tape_count) {
				next_line_start ++; // Skip the newline
				U8     next_line_end = next_line_start;
				while (next_line_end < tape_count && unpack_tag(tape_ptr[next_line_end]) != STag_Format) next_line_end ++;
				U8     next_line_len = next_line_end - next_line_start;
				cursor_idx = next_line_start + (col < next_line_len ? col : next_line_len);
			}
		}
		
		if (wparam == MS_VK_PRIOR) { scroll_y_offset -= 100; if (scroll_y_offset < 0) scroll_y_offset = 0; }
		if (wparam == MS_VK_NEXT)  { scroll_y_offset += 100; }
		if (wparam == MS_VK_F5)    { run_full = !run_full; }

		if (wparam == MS_VK_TAB) {
			// Cycle Color Tag
			U4 t   = tape_ptr[cursor_idx];
			U4 tag = (unpack_tag(t) + 1) % STag_Count;
			tape_ptr[cursor_idx] = pack_token(tag, unpack_val(t));
		}
		else if (wparam == MS_VK_BACK) 
		{
			// Delete Token
			// Shift: delete AT cursor | Regular: delete TO THE LEFT
			U8 delete_idx = cursor_idx;
			B4 is_shift = (ms_get_async_key_state(MS_VK_SHIFT) & 0x8000) != 0;
			if (is_shift == false) {
				if (cursor_idx > 0) {
					delete_idx = cursor_idx - 1;
					cursor_idx--;
				}
				else return 0;
			}
			if (tape_count > 0) {
				U8*r anno_ptr = u8_r(anno_arena.start);
				for (U8 i = delete_idx; i < tape_count - 1; i ++) {
					tape_ptr[i] = tape_ptr[i + 1];
					anno_ptr[i] = anno_ptr[i + 1];
				}
				tape_arena.used -= sizeof(U4);
				anno_arena.used -= sizeof(U8);
			}
		} 
		else if (wparam == MS_VK_SPACE || wparam == MS_VK_RETURN) {
			// Insert New Token
			// Shift: insert AFTER cursor | Regular: insert BEFORE cursor
			B4 is_shift   = (ms_get_async_key_state(MS_VK_SHIFT) & 0x8000) != 0;
			U8 insert_idx = cursor_idx;
			if (is_shift) insert_idx ++;

			if (tape_arena.used + sizeof(U4) <= tape_arena.capacity && anno_arena.used + sizeof(U8) <= anno_arena.capacity) {
				U8*r anno_ptr = u8_r(anno_arena.start);
				for (U8 i = tape_count; i > insert_idx; i --) {
					tape_ptr[i] = tape_ptr[i-1];
					anno_ptr[i] = anno_ptr[i-1];
				}
				if (wparam == MS_VK_RETURN) {
					tape_ptr[insert_idx] = pack_token(STag_Format, 0xA);
					anno_ptr[insert_idx] = 0;
				} else {
					tape_ptr[insert_idx] = pack_token(STag_Comment, id2(' ',' '));
					anno_ptr[insert_idx] = 0;
				}
				if (is_shift) cursor_idx ++;
				tape_arena.used += sizeof(U4);
				anno_arena.used += sizeof(U8);
			}
		}
		
		// Interaction: Reset VM and compile
		vm_rax = 0; vm_rdx = 0;
		mem_zero(u8_(vm_globals), sizeof(vm_globals));
		
		compile_and_run_tape();
		
		ms_invalidate_rect(hwnd, nullptr, true);
		return 0;
	}
	case MS_WM_PAINT: {
			MS_PAINTSTRUCT ps;
			void* hdc      = ms_begin_paint(hwnd, & ps);
			void* hFont    = ms_create_font_a(20, 0, 0, 0, 400, 0, 0, 0, 0, 0, 0, 0, 0, "Consolas");
			void* hOldFont = ms_select_object(hdc, hFont);

			ms_set_bk_mode(hdc, 1); // TRANSPARENT text background

			void* hBgBrush = ms_create_solid_brush(0x00222222);
			ms_select_object(hdc, hBgBrush);
			ms_rectangle(hdc, -1, -1, 3000, 3000);

			void* hBrushEdit = ms_create_solid_brush(0x008E563B);
			void* hBrushNav  = ms_create_solid_brush(0x00262F3B);

			S4 start_x = 40, start_y = 60, spacing_x = 110, spacing_y = 35;
			S4 x = start_x, y = start_y;

			U4*r tape_ptr = u4_r(tape_arena.start);
			U8*r anno_ptr = u8_r(anno_arena.start);

			// Render Tokens
			for (U8 i = 0; i < tape_count; i++) 
			{
				if (x >= start_x + (TOKENS_PER_ROW * spacing_x)) {
					x = start_x; y += spacing_y;
				}
				S4 render_y = y - scroll_y_offset;
				if (i == cursor_idx && render_y >= 30 && render_y < 500) {
					ms_select_object(hdc, editor_mode == MODE_EDIT ? hBrushEdit : hBrushNav);
					ms_rectangle(hdc, x - 5, render_y - 2, x + 95, render_y + 22);
				}
				if (render_y >= 30 && render_y < 500) 
				{
					U4 t    = tape_ptr[i];
					U4 tag  = unpack_tag(t);
					U4 val  = unpack_val(t);
					U8 anno = anno_ptr[i];
					if (tag == STag_Format && val == 0xA) {
						ms_set_text_color(hdc, 0x00444444);
						ms_text_out_a(hdc, x, render_y, "  \\n  ", 6);
						x  = start_x;
						y += spacing_y;
					} 
					else 
					{
						U4          color  = tag_colors[tag];
						const char* prefix = tag_prefixes[tag];
							
						ms_set_text_color(hdc, color);
						if (editor_mode == MODE_EDIT && i == cursor_idx) {
							// Better visibility in Edit Mode: White text on White-ish cursor
							ms_set_text_color(hdc, 0x001E1E1E); 
						}
								
						char val_str[9]; 
						if (tag == STag_Data) {
							u64_to_hex(val, val_str, 6);
							val_str[6] = '\0';
						} 
						else 
						{
							// Extract annotation string
							char* a_str = (char*) & anno;
							if (a_str[0] == '\0') {
								// Fallback to 2-character ID
								val_str[0] = (char)((val >> 8) & 0xFF);
								val_str[1] = (char)(val & 0xFF);
								val_str[2] = ' '; val_str[3] = ' '; val_str[4] = ' '; val_str[5] = ' ';
								val_str[6] = '\0';
							}
							else {
								mem_copy(u8_(val_str), u8_(a_str), 8);
								val_str[8] = '\0';
							}
						}
						char out_buf[12];
						out_buf[0] = prefix[0];
						out_buf[1] = ' ';
						mem_copy(u8_(out_buf + 2), u8_(val_str), 8);
						out_buf[10] = '\0';
						
						ms_text_out_a(hdc, x, render_y, out_buf, 10);
						x += spacing_x;
					}
				}
				else if (unpack_tag(tape_ptr[i]) == STag_Format && unpack_val(tape_ptr[i]) == 0xA) {
					x  = start_x;
					y += spacing_y;
				} 
				else {
					x += spacing_x;
				}
			}

			// Draw a solid background behind the HUD to cover scrolling text
			void* hHudBrush = ms_create_solid_brush(0x00141E23);
			ms_select_object(hdc, hHudBrush);
			ms_rectangle(hdc, -1, 500, 3000, 3000);
			ms_rectangle(hdc, -1, -1, 3000, 40);

			ms_set_text_color(hdc, 0x00AAAAAA);
			ms_text_out_a(hdc, 40, 10, "x86-64 Machine Code Emitter | 2-Reg Stack | [F5] Toggle Run Mode | [PgUp/PgDn] Scroll", 85);
			
			// Render VM State
			ms_set_text_color(hdc, 0x00FFFFFF);
			char jit_str[64] = "Mode: Incremental | JIT Size: 0x000 bytes";
			if (run_full) mem_copy(u8_(jit_str + 6), u8_("Full       "), 11);
			u64_to_hex(code_arena.used, jit_str + 32, 3);
			ms_text_out_a(hdc, 40, 520, jit_str, 41);
			
			char state_str[64] = "RAX: 00000000 | RDX: 00000000";
			u64_to_hex(vm_rax, state_str + 5, 8);
			u64_to_hex(vm_rdx, state_str + 21, 8);
			ms_set_text_color(hdc, 0x0094BAA1); // Number green
			ms_text_out_a(hdc, 40, 550, state_str, 29);

			// HUD: Display Current Token Meaning
			if (tape_count > 0 && cursor_idx < tape_count) {
				U4          cur_tag  = unpack_tag(tape_ptr[cursor_idx]);
				const char* tag_name = tag_names [cur_tag];
				U4         cur_color = tag_colors[cur_tag];
				char semantics_str[64] = "Current Tag: ";
				U4   name_len          = 0;
				while (tag_name[name_len]) {
					semantics_str[13 + name_len] = tag_name[name_len];
					name_len ++;
				}
				semantics_str[13 + name_len] = '\0';
				ms_set_text_color(hdc, cur_color);
				ms_text_out_a(hdc, 40, 580, semantics_str, 13 + name_len);
			}

			ms_set_text_color(hdc, 0x00C8C8C8);
			ms_text_out_a(hdc, 400, 520, "Global Memory (Contiguous Array):", 33);
			for (int i=0; i < 4; i ++) {
				char glob_str[32] = "[0]: 00000000";
				glob_str[1] = '0' + i;
				u64_to_hex(vm_globals[i], glob_str + 5, 8);
				ms_set_text_color(hdc, 0x00D6A454); // Soft blue
				ms_text_out_a(hdc, 400, 550 + (i * 25), glob_str, 13);
			}

			// Print Log
			ms_set_text_color(hdc, 0x00C8C8C8);
			ms_text_out_a(hdc, 750, 520, "Print Log:", 10);
			for (int i = 0; i<log_count && i < 4; i ++) {
				char log_str[32] = "00000000";
				u64_to_hex(log_buffer[i], log_str, 8);
				ms_set_text_color(hdc, 0x0094BAA1);
				ms_text_out_a(hdc, 750, 550 + (i * 25), log_str, 8);
			}    
			ms_select_object(hdc, hOldFont);
			ms_delete_object(hBgBrush);
			ms_delete_object(hBrushEdit);
			ms_delete_object(hBrushNav);
			ms_delete_object(hHudBrush);
			ms_end_paint(hwnd, & ps);
			return 0;
	}
	case MS_WM_DESTROY: { ms_post_quit_message(0); return 0; }
	}
	return ms_def_window_proc_a(hwnd, msg, wparam, lparam);
}

int main(void) {
	// 1. Initialize Memory Arenas using WinAPI + FArena
	Slice tape_mem = slice_ut_(u8_(ms_virtual_alloc(nullptr, 64 * 1024, MS_MEM_COMMIT | MS_MEM_RESERVE, MS_PAGE_READWRITE)), 64 * 1024);
	Slice anno_mem = slice_ut_(u8_(ms_virtual_alloc(nullptr, 64 * 1024, MS_MEM_COMMIT | MS_MEM_RESERVE, MS_PAGE_READWRITE)), 64 * 1024);
	Slice code_mem = slice_ut_(u8_(ms_virtual_alloc(nullptr, 64 * 1024, MS_MEM_COMMIT | MS_MEM_RESERVE, MS_PAGE_EXECUTE_READWRITE)), 64 * 1024);
	if (! tape_mem.ptr || ! anno_mem.ptr || ! code_mem.ptr) ms_exit_process(1);
	
	farena_init(& tape_arena, tape_mem);
	farena_init(& anno_arena, anno_mem);
	farena_init(& code_arena, code_mem);
	
	// Bootstrap Robust Sample: Factorial State Machine
	scatter(pack_token(STag_Comment, id2('I','N')), "INIT    "); // .IN
	scatter(pack_token(STag_Data,    5),            0);      // $5
	scatter(pack_token(STag_Data,    0),            0);      // $0 (Addr)
	scatter(pack_token(STag_Imm,     id2('!',' ')), "STORE   ");    // ^! 
	
	scatter(pack_token(STag_Data,    1),            0);      // $1
	scatter(pack_token(STag_Data,    1),            0);      // $1 (Addr)
	scatter(pack_token(STag_Imm,     id2('!',' ')), "STORE   ");    // ^! 
	
	scatter(pack_token(STag_Format,  0xA),          0); // Newline
	
	// Define the FS (Factorial Step) word in memory
	scatter(pack_token(STag_Define,  id2('F','S')), "F_STEP  "); 
	scatter(pack_token(STag_Data,    0),            0);  
	scatter(pack_token(STag_Call,    id2('@',' ')), "FETCH   "); 
	scatter(pack_token(STag_Call,    id2('R','0')), "RET_IF_Z"); 
	scatter(pack_token(STag_Format,  0xA),          0); // Newline
	
	scatter(pack_token(STag_Data,    1),            0);  
	scatter(pack_token(STag_Call,    id2('@',' ')), "FETCH   "); 
	scatter(pack_token(STag_Data,    0),            0);  
	scatter(pack_token(STag_Call,    id2('@',' ')), "FETCH   "); 
	scatter(pack_token(STag_Call,    id2('M','*')), "MULT    "); 
	scatter(pack_token(STag_Data,    1),            0);  
	scatter(pack_token(STag_Call,    id2('!',' ')), "STORE   "); 
	scatter(pack_token(STag_Format,  0xA),          0); // Newline
	
	scatter(pack_token(STag_Data,    0),            0);  
	scatter(pack_token(STag_Call,    id2('@',' ')), "FETCH   "); 
	scatter(pack_token(STag_Call,    id2('-','1')), "DEC     "); 
	scatter(pack_token(STag_Data,    0),            0);  
	scatter(pack_token(STag_Call,    id2('!',' ')), "STORE   "); 
	
	scatter(pack_token(STag_Data,    1),            0);  
	scatter(pack_token(STag_Call,    id2('@',' ')), "FETCH   "); 
	scatter(pack_token(STag_Call,    id2('P','R')), "PRINT   "); // Print Accumulator!
	
	scatter(pack_token(STag_Call,    id2('R','E')), "RETURN  "); // Return!
	
	scatter(pack_token(STag_Format,  0xA),          0); // Newline
	
	// Call it
	scatter(pack_token(STag_Imm,     id2('F','S')), "F_STEP  ");   // ^FS
	scatter(pack_token(STag_Imm,     id2('F','S')), "F_STEP  ");   
	scatter(pack_token(STag_Imm,     id2('F','S')), "F_STEP  ");   
	scatter(pack_token(STag_Imm,     id2('F','S')), "F_STEP  ");   
	scatter(pack_token(STag_Imm,     id2('F','S')), "F_STEP  ");   

	MS_WNDCLASSA wc;
	mem_fill(u8_(& wc), 0, sizeof(wc));
	wc.lpfnWndProc = win_proc;
	wc.hInstance = ms_get_stock_object(0);
	wc.lpszClassName = "ColorForthWindow";
	wc.hbrBackground = ms_get_stock_object(4);
	ms_register_class_a(& wc);

	void* hwnd = ms_create_window_ex_a(0, wc.lpszClassName, "Sourceless Global Memory Explorer", MS_WS_OVERLAPPEDWINDOW | MS_WS_VISIBLE, 100, 100, 1100, 750, nullptr, nullptr, wc.hInstance, nullptr);
	MS_MSG msg;
	while (ms_get_message_a(& msg, nullptr, 0, 0)) { ms_translate_message(& msg); ms_dispatch_message_a(& msg); }
	ms_exit_process(0);
	return 0;
}
